通用PopupWindow稼虎,幾行代碼搞定PopupWindow彈窗

我們都知道杨何,Android 中的彈窗基本有兩種糕篇,一種是AlertDialog火诸,另一種是PopupWindow躲查,AlertDialog的顯示位置是固定的它浅,PopWindow 的顯示位置是我們可以設(shè)置和調(diào)整的,因此镣煮,像項目中的一些場景如:某個功能的提示說明姐霍、點擊按鈕在按鈕上方或者下方彈出菜單、新功能彈窗引導(dǎo)等典唇。由于這些彈窗的位置不固定镊折,因此都可以用PopupWindow來做。最近項目中也用到了PopupWindow彈窗功能介衔,在寫的過程中恨胚,發(fā)現(xiàn)雖然API比較簡單,但是寫一個PopupWindow炎咖,還是有點繁瑣赃泡,很多重復(fù)代碼,決定簡單封裝一下乘盼,以后添加一個彈窗就不用那么麻煩了升熊,幾行代碼就搞定了。本篇文章是對項目中使用 PopupWindow做一個筆記和總結(jié)绸栅。

一级野、首先先看一下效果圖

效果圖:

popWindow.gif

以上示例展示了4中PopupWindow 使用場景

Github地址:CustomPopWindow

二、PopupWindow 簡單介紹

首先看一下Google 官方文檔對PopupWindow的介紹:

This class represents a popup window that can be used to display an arbitrary view. The popup window is a floating container that appears on top of the current activity.

大致意思就是:popup window 是一個出現(xiàn)在當(dāng)前Activity頂層的懸浮容器粹胯,可以用來展示任意的View蓖柔。

因此只要是個View,都可以用PopupWindow 來展示矛双。這里我就不過多介紹PopupWindow 的API渊抽,挑幾個重要并且常用的說一下:

1,構(gòu)造函數(shù),這個不用多說议忽,多個重載函數(shù)懒闷,穿不同的參數(shù)。

public PopupWindow(int width, int height)

public PopupWindow(View contentView, int width, int height) 

public PopupWindow(View contentView, int width, int height, boolean focusable) 

2,設(shè)置顯示的View:

public void setContentView(View contentView)

3,設(shè)置展示的寬愤估、高帮辟,構(gòu)造函數(shù)傳了寬高就不用重新設(shè)置

// 設(shè)置寬,其實構(gòu)造函數(shù)也是調(diào)用的這個方法
public void setWidth(int width)
//設(shè)置高
public void setHeight(int height)

4玩焰,設(shè)置是否獲取焦點

public void setFocusable(boolean focusable)

5,設(shè)置點擊PopupWindow 以外區(qū)域是否可以隱藏PopupWindow

public void setOutsideTouchable(boolean touchable)

注意:這里要注意一下由驹,有時侯我們希望觸摸PopupWindow 以外區(qū)域就隱藏PopupWindow,理論上我們只需要調(diào)用 setOutsideTouchable(ture)設(shè)置為ture就可以了,但是實際上只設(shè)置這個屬性是不行的昔园,必須設(shè)置背景蔓榄,也就是說要和setBackgroundDrawable(Drawable background)同時使用才有效,不然默刚,點擊PopupWindow以外區(qū)域是不能隱藏掉的甥郑。

6,隱藏PopupWindow

public void dismiss()

7,設(shè)置dissmiss 回調(diào)監(jiān)聽

public void setOnDismissListener(OnDismissListener onDismissListener)

8,顯示PopupWindow

//直接顯示在參照View 的左下方
public void showAsDropDown(View anchor)
// 顯示在參照View的左下方荤西,可以通過xoff澜搅,yOff,來調(diào)節(jié)x,y方向的偏移
public void showAsDropDown(View anchor, int xoff, int off)

public void showAsDropDown(View anchor, int xoff, int yoff, int gravity)
//顯示在指定位置,相對于整個屏幕的window而言邪锌,通過gravity調(diào)解顯示在左勉躺、上、右觅丰、下饵溅、中. x,y調(diào)整兩個方向的偏移
public void showAtLocation(View parent, int gravity, int x, int y) 

以上就是PopupWindow 重要的并且常用的API。

三妇萄、封裝通用PopupWindow,CustomPopWindow,使用鏈?zhǔn)降姆绞脚渲貌@示

由于每次寫PopupWindow都要寫很多重復(fù)代碼概说,因此簡單的封裝了一個CustomPopWindow.封裝了PopupWindow 的一些常用API,使用Builder模式嚣伐,就像寫AlertDialog 一樣,鏈?zhǔn)脚渲谩?/p>

使用方法:
1萍丐,簡便寫法:

CustomPopWindow popWindow = new CustomPopWindow.PopupWindowBuilder(this)
                .setView(R.layout.pop_layout1)//顯示的布局轩端,還可以通過設(shè)置一個View
           //     .size(600,400) //設(shè)置顯示的大小,不設(shè)置就默認(rèn)包裹內(nèi)容
                .setFocusable(true)//是否獲取焦點逝变,默認(rèn)為ture
                .setOutsideTouchable(true)//是否PopupWindow 以外觸摸dissmiss
                .create()//創(chuàng)建PopupWindow
                .showAsDropDown(mButton1,0,10);//顯示PopupWindow

以上就是彈出一個簡單的PopupWindow基茵,是不是看起來很優(yōu)雅和簡單,還可以簡單一點:

CustomPopWindow popWindow = new CustomPopWindow.PopupWindowBuilder(this)
                .setView(R.layout.pop_layout1)//顯示的布局
                .create()//創(chuàng)建PopupWindow
                .showAsDropDown(mButton1,0,10);//顯示PopupWindow

如果是一個簡單的只展示文案的彈窗壳影,就可以只設(shè)置一個View拱层,就可以了,很簡單吧Q邕帧8啤!

2,展示一個PopupWindow 彈窗菜單(像手機QQ烙肺,微信的頂部菜單)

View contentView = LayoutInflater.from(this).inflate(R.layout.pop_menu,null);
        //處理popWindow 顯示內(nèi)容
        handleLogic(contentView);
        //創(chuàng)建并顯示popWindow
        mCustomPopWindow= new CustomPopWindow.PopupWindowBuilder(this)
                .setView(contentView)
                .create()
                .showAsDropDown(mButton3,0,20);

如果PopupWindow 展示的內(nèi)容需要在程序代碼中設(shè)置或者響應(yīng)點擊事件等纳猪,可以現(xiàn)獲取到這個View,然后處理一些顯示和點擊事件邏輯桃笙,再交給CustomPopWindow 創(chuàng)建顯示氏堤。比如響應(yīng)菜單點擊事件的邏輯處理:

 /**
     * 處理彈出顯示內(nèi)容、點擊事件等邏輯
     * @param contentView
     */
    private void handleLogic(View contentView){
        View.OnClickListener listener = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(mCustomPopWindow!=null){
                    mCustomPopWindow.dissmiss();
                }
                String showContent = "";
                switch (v.getId()){
                    case R.id.menu1:
                        showContent = "點擊 Item菜單1";
                        break;
                    case R.id.menu2:
                        showContent = "點擊 Item菜單2";
                        break;
                    case R.id.menu3:
                        showContent = "點擊 Item菜單3";
                        break;
                    case R.id.menu4:
                        showContent = "點擊 Item菜單4";
                        break;
                    case R.id.menu5:
                        showContent = "點擊 Item菜單5" ;
                        break;
                }
                Toast.makeText(MainActivity.this,showContent,Toast.LENGTH_SHORT).show();
            }
        };
        contentView.findViewById(R.id.menu1).setOnClickListener(listener);
        contentView.findViewById(R.id.menu2).setOnClickListener(listener);
        contentView.findViewById(R.id.menu3).setOnClickListener(listener);
        contentView.findViewById(R.id.menu4).setOnClickListener(listener);
        contentView.findViewById(R.id.menu5).setOnClickListener(listener);
    }
}

3,展示一個ListView,其實跟上面是一樣的搏明,這里貼一下實例代碼:

private void showPopListView(){
        View contentView = LayoutInflater.from(this).inflate(R.layout.pop_list,null);
        //處理popWindow 顯示內(nèi)容
        handleListView(contentView);
        //創(chuàng)建并顯示popWindow
        mListPopWindow= new CustomPopWindow.PopupWindowBuilder(this)
                .setView(contentView)
                .size(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT)//顯示大小
                .create()
                .showAsDropDown(mButton4,0,20);
    }

    private void handleListView(View contentView){
        RecyclerView recyclerView = (RecyclerView) contentView.findViewById(R.id.recyclerView);
        LinearLayoutManager manager = new LinearLayoutManager(this);
        manager.setOrientation(LinearLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(manager);
        MyAdapter adapter = new MyAdapter();
        adapter.setData(mockData());
        recyclerView.setAdapter(adapter);
        adapter.notifyDataSetChanged();

    }

以上就是對于 PopupWindow的封裝和使用示例鼠锈。封裝了PopupWindow 常用的API,沒有展示完全星著,還有像添加顯示和隱藏動畫setAnimationStyle(int animationStyle)购笆,隱藏回調(diào)監(jiān)聽等等。需要的朋友自己試試强饮。

最后由桌,貼出CustomPopWindow的源碼:

/**
 *
 * 自定義PopWindow類,封裝了PopWindow的一些常用屬性邮丰,用Builder模式支持鏈?zhǔn)秸{(diào)用
 * Created by zhouwei on 16/11/28.
 */

public class CustomPopWindow {
    private Context mContext;
    private int mWidth;
    private int mHeight;
    private boolean mIsFocusable = true;
    private boolean mIsOutside = true;
    private int mResLayoutId = -1;
    private View mContentView;
    private PopupWindow mPopupWindow;
    private int mAnimationStyle = -1;

    private boolean mClippEnable = true;//default is true
    private boolean mIgnoreCheekPress = false;
    private int mInputMode = -1;
    private PopupWindow.OnDismissListener mOnDismissListener;
    private int mSoftInputMode = -1;
    private boolean mTouchable = true;//default is ture
    private View.OnTouchListener mOnTouchListener;
    private CustomPopWindow(Context context){
        mContext = context;
    }

    public int getWidth() {
        return mWidth;
    }

    public int getHeight() {
        return mHeight;
    }

    /**
     *
     * @param anchor
     * @param xOff
     * @param yOff
     * @return
     */
    public CustomPopWindow showAsDropDown(View anchor, int xOff, int yOff){
        if(mPopupWindow!=null){
            mPopupWindow.showAsDropDown(anchor,xOff,yOff);
        }
        return this;
    }

    public CustomPopWindow showAsDropDown(View anchor){
        if(mPopupWindow!=null){
            mPopupWindow.showAsDropDown(anchor);
        }
        return this;
    }

    @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    public CustomPopWindow showAsDropDown(View anchor, int xOff, int yOff, int gravity){
        if(mPopupWindow!=null){
            mPopupWindow.showAsDropDown(anchor,xOff,yOff,gravity);
        }
        return this;
    }


    /**
     * 相對于父控件的位置(通過設(shè)置Gravity.CENTER行您,下方Gravity.BOTTOM等 ),可以設(shè)置具體位置坐標(biāo)
     * @param parent 
     * @param gravity
     * @param x the popup's x location offset
     * @param y the popup's y location offset
     * @return
     */
    public CustomPopWindow showAtLocation(View parent, int gravity, int x, int y){
        if(mPopupWindow!=null){
            mPopupWindow.showAtLocation(parent,gravity,x,y);
        }
        return this;
    }

    /**
     * 添加一些屬性設(shè)置
     * @param popupWindow
     */
    private void apply(PopupWindow popupWindow){
        popupWindow.setClippingEnabled(mClippEnable);
        if(mIgnoreCheekPress){
            popupWindow.setIgnoreCheekPress();
        }
        if(mInputMode!=-1){
            popupWindow.setInputMethodMode(mInputMode);
        }
        if(mSoftInputMode!=-1){
            popupWindow.setSoftInputMode(mSoftInputMode);
        }
        if(mOnDismissListener!=null){
            popupWindow.setOnDismissListener(mOnDismissListener);
        }
        if(mOnTouchListener!=null){
            popupWindow.setTouchInterceptor(mOnTouchListener);
        }
        popupWindow.setTouchable(mTouchable);



    }

    private PopupWindow build(){

        if(mContentView == null){
            mContentView = LayoutInflater.from(mContext).inflate(mResLayoutId,null);
        }

        if(mWidth != 0 && mHeight!=0 ){
            mPopupWindow = new PopupWindow(mContentView,mWidth,mHeight);
        }else{
            mPopupWindow = new PopupWindow(mContentView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        }
        if(mAnimationStyle!=-1){
            mPopupWindow.setAnimationStyle(mAnimationStyle);
        }

        apply(mPopupWindow);//設(shè)置一些屬性

        mPopupWindow.setFocusable(mIsFocusable);
        mPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        mPopupWindow.setOutsideTouchable(mIsOutside);

        if(mWidth == 0 || mHeight == 0){
            mPopupWindow.getContentView().measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
            //如果外面沒有設(shè)置寬高的情況下剪廉,計算寬高并賦值
            mWidth = mPopupWindow.getContentView().getMeasuredWidth();
            mHeight = mPopupWindow.getContentView().getMeasuredHeight();
        }

        mPopupWindow.update();

        return mPopupWindow;
    }

    /**
     * 關(guān)閉popWindow
     */
    public void dissmiss(){
        if(mPopupWindow!=null){
            mPopupWindow.dismiss();
        }
    }


    public static class PopupWindowBuilder{
        private CustomPopWindow mCustomPopWindow;

        public PopupWindowBuilder(Context context){
            mCustomPopWindow = new CustomPopWindow(context);
        }
        public PopupWindowBuilder size(int width,int height){
            mCustomPopWindow.mWidth = width;
            mCustomPopWindow.mHeight = height;
            return this;
        }


        public PopupWindowBuilder setFocusable(boolean focusable){
            mCustomPopWindow.mIsFocusable = focusable;
            return this;
        }



        public PopupWindowBuilder setView(int resLayoutId){
            mCustomPopWindow.mResLayoutId = resLayoutId;
            mCustomPopWindow.mContentView = null;
            return this;
        }

        public PopupWindowBuilder setView(View view){
            mCustomPopWindow.mContentView = view;
            mCustomPopWindow.mResLayoutId = -1;
            return this;
        }

        public PopupWindowBuilder setOutsideTouchable(boolean outsideTouchable){
            mCustomPopWindow.mIsOutside = outsideTouchable;
            return this;
        }

        /**
         * 設(shè)置彈窗動畫
         * @param animationStyle
         * @return
         */
        public PopupWindowBuilder setAnimationStyle(int animationStyle){
            mCustomPopWindow.mAnimationStyle = animationStyle;
            return this;
        }


        public PopupWindowBuilder setClippingEnable(boolean enable){
            mCustomPopWindow.mClippEnable =enable;
            return this;
        }


        public PopupWindowBuilder setIgnoreCheekPress(boolean ignoreCheekPress){
            mCustomPopWindow.mIgnoreCheekPress = ignoreCheekPress;
            return this;
        }

        public PopupWindowBuilder setInputMethodMode(int mode){
            mCustomPopWindow.mInputMode = mode;
            return this;
        }

        public PopupWindowBuilder setOnDissmissListener(PopupWindow.OnDismissListener onDissmissListener){
            mCustomPopWindow.mOnDismissListener = onDissmissListener;
            return this;
        }


        public PopupWindowBuilder setSoftInputMode(int softInputMode){
            mCustomPopWindow.mSoftInputMode = softInputMode;
            return this;
        }


        public PopupWindowBuilder setTouchable(boolean touchable){
            mCustomPopWindow.mTouchable = touchable;
            return this;
        }

        public PopupWindowBuilder setTouchIntercepter(View.OnTouchListener touchIntercepter){
            mCustomPopWindow.mOnTouchListener = touchIntercepter;
            return this;
        }


        public CustomPopWindow create(){
            //構(gòu)建PopWindow
            mCustomPopWindow.build();
            return mCustomPopWindow;
        }

    }

}

好了娃循,本篇文章到此結(jié)束,以上就是PopupWindow的一些介紹和一個簡單的封裝斗蒋。要源碼的同學(xué)捌斧,請看 Github地址:CustomWindow,如果覺得對你有用的話泉沾,歡迎start捞蚂。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市跷究,隨后出現(xiàn)的幾起案子姓迅,更是在濱河造成了極大的恐慌,老刑警劉巖俊马,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丁存,死亡現(xiàn)場離奇詭異,居然都是意外死亡柴我,警方通過查閱死者的電腦和手機解寝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來艘儒,“玉大人聋伦,你說我怎么就攤上這事夫偶。” “怎么了嘉抓?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵索守,是天一觀的道長。 經(jīng)常有香客問我抑片,道長卵佛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任敞斋,我火速辦了婚禮截汪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘植捎。我一直安慰自己衙解,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布焰枢。 她就那樣靜靜地躺著蚓峦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪济锄。 梳的紋絲不亂的頭發(fā)上暑椰,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音荐绝,去河邊找鬼一汽。 笑死,一個胖子當(dāng)著我的面吹牛低滩,可吹牛的內(nèi)容都是我干的召夹。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼恕沫,長吁一口氣:“原來是場噩夢啊……” “哼监憎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起婶溯,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤枫虏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后爬虱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡腾它,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年跑筝,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瞒滴。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡曲梗,死狀恐怖赞警,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情虏两,我是刑警寧澤愧旦,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站定罢,受9級特大地震影響笤虫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜祖凫,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一琼蚯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧惠况,春花似錦遭庶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至权埠,卻和暖如春榨了,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背弊知。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工阻逮, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人秩彤。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓叔扼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親漫雷。 傳聞我的和親對象是個殘疾皇子瓜富,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容