Android彈窗探究之PopupWindow的使用

相對(duì)于AlertDialog的使用粮坞,PopupWindow的使用也比較簡(jiǎn)單,這里主要介紹的是PopupWindow的基礎(chǔ)使用包括在使用過程中的一些注意事項(xiàng)初狰,做個(gè)筆記莫杈。

效果展示:

1.gif

PopupWindow的基礎(chǔ)方法

創(chuàng)建popupWindow實(shí)例
popupView = LayoutInflater.from(this).inflate(R.layout.popupwindow_view, null)
popupWindow = PopupWindow()
設(shè)置展示的視圖
// 設(shè)置PopupWindow裝載的視圖
// 也可以在創(chuàng)建popupWindow的時(shí)候直接設(shè)置進(jìn)去
popupWindow?.contentView = popupView
設(shè)置寬度和高度
// 設(shè)置PopupWindow的寬高
// 1》自定義View的時(shí)候,最外層的布局設(shè)置的寬高無效
// 2》在外面必須手動(dòng)設(shè)置寬度和高度奢入,并且以外面設(shè)置的寬高為主
popupWindow?.width = ViewGroup.LayoutParams.WRAP_CONTENT
popupWindow?.height = ViewGroup.LayoutParams.WRAP_CONTENT
設(shè)置外部區(qū)域可以點(diǎn)擊取消popupWindow
// 設(shè)置外部區(qū)域可以點(diǎn)擊取消popupWindow
popupWindow?.isOutsideTouchable = true
設(shè)置背景
// 設(shè)置PopupWindow的背景
popupWindow?.setBackgroundDrawable(resources.getDrawable(R.mipmap.ic_launcher))
設(shè)置popupWindow是否可以聚焦
// 設(shè)置PopupWindow可以聚焦
// 如果不設(shè)置筝闹,在PopupWindow彈出的時(shí)候,點(diǎn)擊返回鍵將直接退出Activity
// 設(shè)置之后腥光,在PopupWindow彈出的時(shí)候关顷,點(diǎn)擊返回鍵不會(huì)直接退出Activity而是關(guān)閉當(dāng)前彈出的PopupWindow
popupWindow?.isFocusable = true
設(shè)置彈窗彈出的動(dòng)畫高度
// 設(shè)置彈窗彈出的動(dòng)畫高度
popupWindow?.elevation = 100f
設(shè)置popupWindow是否可以觸摸
// 設(shè)置PopupWindow可以觸摸
popupWindow?.isTouchable = true
設(shè)置觸摸監(jiān)聽
// 設(shè)置觸摸監(jiān)聽
popupWindow?.setTouchInterceptor { _, _ ->
    Toast.makeText(this,"觸發(fā)事件",Toast.LENGTH_SHORT).show()
    false
}
設(shè)置取消事件監(jiān)聽
// 設(shè)置PopupWindow監(jiān)聽取消事件
popupWindow?.setOnDismissListener {
    Toast.makeText(this,"PopupWindow被關(guān)閉",Toast.LENGTH_SHORT).show()
}

PopupWindow的展示

方式一:

// 方法一:showAsDropDown(View anchor)
//        showAsDropDown(View anchor, int xoff, int yoff)
//        showAsDropDown(View anchor, int xoff, int yoff, int gravity)
//        anchor代表的是目標(biāo)View,即參考的View
//        xoff,yoff     目標(biāo)View的坐標(biāo)偏移量
//        int gravity   目標(biāo)View的位置武福,默認(rèn)為Gravity.TOP | Gravity.START议双,即以左上角為起始位置
popupWindow?.showAsDropDown(view,0,0, Gravity.BOTTOM)

方式二:

// 方法二:showAtLocation(View parent, int gravity, int x, int y)
//        View parent代表的是要能獲取到window唯一標(biāo)示的(也就是只要能獲取到window 標(biāo)示,view是什么控件都可以)
//        int gravity代表的是位置艘儒,即屏幕的上下左右聋伦,注意系統(tǒng)默認(rèn)都是在屏幕中間
//         int x, int y偏移量
popupWindow?.showAtLocation(view, Gravity.TOP, 0, 0)

當(dāng)然,在實(shí)際的開發(fā)過程中我們并不能僅僅滿足于如何簡(jiǎn)單使用界睁,更多的時(shí)候我們需要去考慮兼容性與擴(kuò)展性的問題,所以兵拢,在這里翻斟,我對(duì)PopupWindow做了一個(gè)簡(jiǎn)單的封裝,如下所示:

簡(jiǎn)單封裝

創(chuàng)建PopupWindow的管理類说铃,即PopupWindowManager類

public class PopupWindowManager {

    // 設(shè)置默認(rèn)的寬高
    private int mPopupWindowWidth = ViewGroup.LayoutParams.MATCH_PARENT;
    private int mPopupWindowHeight = ViewGroup.LayoutParams.WRAP_CONTENT;

    private MyPopupWindow mPopupWindow;
    private static volatile PopupWindowManager mInstance;

    /**
     * 通過懶漢式來構(gòu)建PopupWindowManager
     *
     * @return PopupWindowManager實(shí)例對(duì)象
     */
    public static PopupWindowManager getInstance() {
        if (mInstance == null) {
            synchronized (PopupWindowManager.class) {
                if (mInstance == null) {
                    mInstance = new PopupWindowManager();
                }
            }
        }
        return mInstance;
    }

    /**
     * 設(shè)置私有的構(gòu)造函數(shù)
     */
    private PopupWindowManager() {
        mPopupWindow = new MyPopupWindow();
    }

    /**
     * 獲取PopupWindow的實(shí)例
     *
     * @return PopupWindow
     */
    private PopupWindow getMyPopupWindow() {
        return mPopupWindow;
    }

    /**
     * 初始化設(shè)置(默認(rèn)寬高)
     *
     * @return PopupWindowManager實(shí)例對(duì)象
     */
    public PopupWindowManager init(View contentView) {
        return init(contentView, mPopupWindowWidth, mPopupWindowHeight);
    }

    /**
     * @param contentView 加載的View
     * @param width       設(shè)置的寬度
     * @param height      設(shè)置的高度
     * @return PopupWindowManager實(shí)例對(duì)象
     * 默認(rèn)情況下:(1)popupWindow點(diǎn)擊外部區(qū)域可以關(guān)閉
     * (2)popupWindow可以聚焦
     * // 設(shè)置PopupWindow可以聚焦
     * // 如果不設(shè)置访惜,在PopupWindow彈出的時(shí)候,點(diǎn)擊返回鍵將直接退出Activity
     * // 設(shè)置之后腻扇,在PopupWindow彈出的時(shí)候债热,點(diǎn)擊返回鍵不會(huì)直接退出Activity而是關(guān)閉當(dāng)前彈出的PopupWindow
     * (3)popupWindow彈出的動(dòng)畫高度為0
     * (4)popupWindow內(nèi)容區(qū)域可以觸摸
     */
    public PopupWindowManager init(View contentView, int width, int height) {
        this.mPopupWindowWidth = width;
        this.mPopupWindowHeight = height;
        mPopupWindow.setContentView(contentView);
        mPopupWindow.setOutsideTouchable(true);
        mPopupWindow.setFocusable(true);
        mPopupWindow.setElevation(0);
        mPopupWindow.setTouchable(true);
        setBackgroundDrawable();
        mPopupWindow.setWidth(mPopupWindowWidth);
        mPopupWindow.setHeight(mPopupWindowHeight);
        mPopupWindow.setAnimationStyle(R.style.popup_window_anim_style);
        return this;
    }

    /**
     * 設(shè)置popupWindow的背景
     *
     * @return PopupWindowManager實(shí)例對(duì)象
     */
    private PopupWindowManager setBackgroundDrawable() {
        ColorDrawable dw = new ColorDrawable(Color.parseColor("#50000000"));
        mPopupWindow.setBackgroundDrawable(dw);
        return this;
    }

    /**
     * 設(shè)置popupWindow的背景
     *
     * @param color 設(shè)置的背景顏色
     * @return PopupWindowManager實(shí)例對(duì)象
     */
    public PopupWindowManager setBackgroundDrawable(int color) {
        ColorDrawable dw = new ColorDrawable(color);
        mPopupWindow.setBackgroundDrawable(dw);
        return this;
    }

    /**
     * 設(shè)置popupWindow的動(dòng)畫效果
     * @param animationStyle 動(dòng)畫樣式
     * @return PopupWindowManager實(shí)例對(duì)象
     */
    public PopupWindowManager setAnimationStyle(int animationStyle){
        mPopupWindow.setAnimationStyle(animationStyle);
        return this;
    }

    /**
     * 設(shè)置popupWindow點(diǎn)擊外部區(qū)域是否可以關(guān)閉
     *
     * @param isOutsideTouchable true代表可以關(guān)閉 false代表不可以關(guān)閉
     * @return PopupWindowManager實(shí)例對(duì)象
     */
    public PopupWindowManager setOutsideTouchable(boolean isOutsideTouchable) {
        mPopupWindow.setOutsideTouchable(isOutsideTouchable);
        return this;
    }

    /**
     * popupWindow是否可以聚焦
     *
     * @param isFocusable true代表可以聚焦 false代表不可以聚焦
     * @return PopupWindowManager實(shí)例對(duì)象
     */
    public PopupWindowManager setFocusable(boolean isFocusable) {
        mPopupWindow.setFocusable(isFocusable);
        return this;
    }

    /**
     * 設(shè)置popupWindow彈出的動(dòng)畫高度
     *
     * @param elevation 高度
     * @return PopupWindowManager實(shí)例對(duì)象
     */
    public PopupWindowManager setElevation(float elevation) {
        mPopupWindow.setElevation(elevation);
        return this;
    }

    /**
     * 設(shè)置popupWindow內(nèi)容區(qū)域是否可以觸摸
     *
     * @param touchable true代表可以觸摸 false代表不可以觸摸
     * @return PopupWindowManager實(shí)例對(duì)象
     */
    public PopupWindowManager setTouchable(boolean touchable) {
        mPopupWindow.setTouchable(touchable);
        return this;
    }

    /**
     * 設(shè)置關(guān)閉PopupWindow的監(jiān)聽
     *
     * @param onDismissListener 設(shè)置的監(jiān)聽實(shí)例
     * @return PopupWindowManager實(shí)例對(duì)象
     */
    public PopupWindowManager setOnDismissListener(PopupWindow.OnDismissListener onDismissListener) {
        mPopupWindow.setOnDismissListener(onDismissListener);
        return this;
    }

    /**
     * 在PopupWindow彈出之后點(diǎn)擊任意區(qū)域關(guān)閉
     *
     * @param listener 設(shè)置的監(jiān)聽實(shí)例
     * @return PopupWindowManager實(shí)例對(duì)象
     */
    public PopupWindowManager setTouchInterceptor(View.OnTouchListener listener) {
        mPopupWindow.setTouchInterceptor(listener);
        return this;
    }

    /**
     * 在指定視圖的下方展示
     *
     * @param anchor 目標(biāo)View
     *               一般情況下展示的視圖以目標(biāo)View的左下方的位置作為錨點(diǎn)
     */
    public void showAsDropDown(View anchor) {
        mPopupWindow.showAsDropDown(anchor);
    }

    /**
     * 在指定視圖的下方展示
     *
     * @param anchor 目標(biāo)View
     * @param xoff   x軸的偏移量
     * @param yoff   y軸的偏移量
     */
    public void showAsDropDown(View anchor, int xoff, int yoff) {
        mPopupWindow.showAsDropDown(anchor, xoff, yoff);
    }

    /**
     * 在指定視圖的下方展示
     *
     * @param anchor  目標(biāo)View
     * @param xoff    x軸的偏移量
     * @param yoff    y軸的偏移量
     * @param gravity 相對(duì)于View的定位,系統(tǒng)默認(rèn)是Gravity.TOP | Gravity.START;
     */
    public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
        mPopupWindow.showAsDropDown(anchor, xoff, yoff, gravity);
    }

    /**
     * 以絕對(duì)值(x,y)來進(jìn)行顯示
     *
     * @param parent  View parent代表的是要能獲取到window唯一標(biāo)示的(也就是只要能獲取到window 標(biāo)示幼苛,view是什么控件都可以)
     * @param gravity int gravity代表的是位置窒篱,即屏幕的上下左右,注意系統(tǒng)默認(rèn)都是在屏幕中間
     * @param x       x軸的偏移量
     * @param y       y軸的偏移量
     */
    public void showAtLocation(View parent, int gravity, int x, int y) {
        mPopupWindow.showAtLocation(parent, gravity, x, y);
    }

    /**
     * 關(guān)閉PopupWindow
     */
    public void close() {
        if (mPopupWindow != null && mPopupWindow.isShowing()) {
            mPopupWindow.dismiss();
        }
    }
}

新建類來繼承自PopupWindow

public class MyPopupWindow extends PopupWindow {


    @Override
    public void showAsDropDown(View anchor) {
        handlerHeight(anchor);
        super.showAsDropDown(anchor);
    }

    @Override
    public void showAsDropDown(View anchor, int xoff, int yoff) {
        handlerHeight(anchor);
        super.showAsDropDown(anchor, xoff, yoff);
    }

    @Override
    public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
        handlerHeight(anchor);
        super.showAsDropDown(anchor, xoff, yoff, gravity);
    }

    /**
     * 解決高度無法自適應(yīng)的問題
     */
    private void handlerHeight(View anchor) {
        if (Build.VERSION.SDK_INT >= 24) {
            Rect rect = new Rect();
            anchor.getGlobalVisibleRect(rect);
            int heightPixels = anchor.getResources().getDisplayMetrics().heightPixels;
            int h = heightPixels - rect.bottom + getStatusHeight(anchor.getContext());
            setHeight(h);
        }
    }

    /**
     * 獲取狀態(tài)欄的高度
     */
    private int getStatusHeight(Context context) {
        int statusHeight = -1;
        try {
            Class<?> clazz = Class.forName("com.android.internal.R$dimen");
            Object object = clazz.newInstance();
            int height = Integer.parseInt(clazz.getField("status_bar_height").get(object).toString());
            statusHeight = context.getResources().getDimensionPixelSize(height);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return statusHeight;
    }
}

最后進(jìn)行調(diào)用:

     val popView = LayoutInflater.from(this).inflate(R.layout.pop_view, null)
     PopupWindowManager.getInstance().init(popView).setOnDismissListener {
        PopupWindowManager.getInstance().close()
      }.setTouchInterceptor { v, event ->
            PopupWindowManager.getInstance().close()
          false
      }.showAsDropDown(view)

到這里舶沿,popupWindow的簡(jiǎn)單使用就完成了墙杯。最后來講一下在使用popupWindow的過程中需要注意的幾點(diǎn)。

注意事項(xiàng)

(1)必須手動(dòng)給popupWindow設(shè)置寬度和高度括荡,否則popupWindow不顯示高镐。
(2)在手機(jī)系統(tǒng)的API大于24的時(shí)候全屏展示的時(shí)候會(huì)完全填充整個(gè)屏幕,而不是在目標(biāo)View的下方正常顯示畸冲。
(3)在有些手機(jī)上面嫉髓,全屏展示的時(shí)候底部會(huì)留白观腊,其實(shí)是因?yàn)镾tatusBar的高度沒有計(jì)算進(jìn)去,需要我們自己手動(dòng)計(jì)算出去算行。
(4)當(dāng)我們?cè)O(shè)置了setFocusable為true的時(shí)候梧油,點(diǎn)擊屏幕事件會(huì)交由onTouchListener處理。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末纱意,一起剝皮案震驚了整個(gè)濱河市婶溯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌偷霉,老刑警劉巖迄委,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異类少,居然都是意外死亡叙身,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門硫狞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來信轿,“玉大人,你說我怎么就攤上這事残吩〔坪觯” “怎么了?”我有些...
    開封第一講書人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵泣侮,是天一觀的道長(zhǎng)即彪。 經(jīng)常有香客問我,道長(zhǎng)活尊,這世上最難降的妖魔是什么隶校? 我笑而不...
    開封第一講書人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮蛹锰,結(jié)果婚禮上深胳,老公的妹妹穿的比我還像新娘。我一直安慰自己铜犬,他們只是感情好舞终,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著翎苫,像睡著了一般权埠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上煎谍,一...
    開封第一講書人閱讀 51,215評(píng)論 1 299
  • 那天攘蔽,我揣著相機(jī)與錄音,去河邊找鬼呐粘。 笑死满俗,一個(gè)胖子當(dāng)著我的面吹牛转捕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播唆垃,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼五芝,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了辕万?” 一聲冷哼從身側(cè)響起枢步,我...
    開封第一講書人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎渐尿,沒想到半個(gè)月后醉途,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡砖茸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年隘擎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凉夯。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡货葬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出劲够,到底是詐尸還是另有隱情震桶,我是刑警寧澤,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布征绎,位于F島的核電站尼夺,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏炒瘸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一寝衫、第九天 我趴在偏房一處隱蔽的房頂上張望顷扩。 院中可真熱鬧,春花似錦慰毅、人聲如沸隘截。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽婶芭。三九已至,卻和暖如春着饥,著一層夾襖步出監(jiān)牢的瞬間犀农,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工宰掉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留呵哨,地道東北人赁濒。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像孟害,于是被迫代替她去往敵國(guó)和親拒炎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354

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