日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

PopUpWindow使用详解(二)——进阶及答疑

發布時間:2025/4/9 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 PopUpWindow使用详解(二)——进阶及答疑 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

相關文章:
1、《PopUpWindow使用詳解(一)——基本使用》
2、《PopUpWindow使用詳解(二)——進階及答疑》

上篇為大家基本講述了有關PopupWindow的基本使用,但還有幾個相關函數還沒有講述,我們這篇將著重看看這幾個函數的用法并結合源碼來講講具體原因,最后是有關PopupWindow在使用時的疑問,給大家講解一下。

一、常用函數講解

這段將會給大家講下下面幾個函數的意義及用法,使用上篇那個帶背景的例子為基礎。

?

?

[java]?view plaincopy
  • public?void?setTouchable(boolean?touchable)??
  • public?void?setFocusable(boolean?focusable)??
  • public?void?setOutsideTouchable(boolean?touchable)??
  • public?void?setBackgroundDrawable(Drawable?background)??
  • 1、setTouchable(boolean touchable)

    設置PopupWindow是否響應touch事件,默認是true,如果設置為false,即會是下面這個結果:(所有touch事件無響應,包括點擊事件)

    ?

    對應代碼:

    ?

    [java]?view plaincopy
  • private?void?showPopupWindow()?{??
  • ????View?contentView?=?LayoutInflater.from(MainActivity.this).inflate(R.layout.popuplayout,?null);??
  • ????mPopWindow?=?new?PopupWindow(contentView);??
  • ????mPopWindow.setWidth(ViewGroup.LayoutParams.FILL_PARENT);??
  • ????mPopWindow.setHeight(ViewGroup.LayoutParams.FILL_PARENT);??
  • ??
  • ????mPopWindow.setTouchable(false);??
  • ??
  • ????………………//單項點擊??
  • ??
  • ????mPopWindow.showAsDropDown(mMenuTv);??
  • }??
  • 2、setFocusable(boolean focusable)

    該函數的意義表示,PopupWindow是否具有獲取焦點的能力,默認為False。一般來講是沒有用的,因為普通的控件是不需要獲取焦點的,而對于EditText則不同,如果不能獲取焦點,那么EditText將是無法編輯的。
    所以,我們在popuplayout.xml最底部添加一個EditText,分別演示兩段不同的代碼,即分別將setFocusable設置為false和設置為true;看看有什么不同:
    (1)setFocusable(true)
    代碼如下:

    ?

    ?

    [html]?view plaincopy
  • private?void?showPopupWindow()?{??
  • ????View?contentView?=?LayoutInflater.from(MainActivity.this).inflate(R.layout.popuplayout,?null);??
  • ????mPopWindow?=?new?PopupWindow(contentView);??
  • ????mPopWindow.setWidth(ViewGroup.LayoutParams.FILL_PARENT);??
  • ????mPopWindow.setHeight(ViewGroup.LayoutParams.FILL_PARENT);??
  • ??
  • ????//是否具有獲取焦點的能力??
  • ????mPopWindow.setFocusable(true);??
  • ??
  • ???…………//各item點擊響應??
  • ??
  • ??
  • ????mPopWindow.showAsDropDown(mMenuTv);??
  • }??
  • 明顯在點擊EditText的時候,會彈出編輯框。

    ?

    (2)setFocusable(false)
    同樣上面一段代碼,那我們將setFocusable設置為false,會是怎樣呢?

    ?

    [java]?view plaincopy
  • private?void?showPopupWindow()?{??
  • ????View?contentView?=?LayoutInflater.from(MainActivity.this).inflate(R.layout.popuplayout,?null);??
  • ????mPopWindow?=?new?PopupWindow(contentView);??
  • ????mPopWindow.setWidth(ViewGroup.LayoutParams.FILL_PARENT);??
  • ????mPopWindow.setHeight(ViewGroup.LayoutParams.FILL_PARENT);??
  • ??
  • ????//是否具有獲取焦點的能力??
  • ????mPopWindow.setFocusable(false);??
  • ??
  • ???…………//各item點擊響應??
  • ??
  • ??
  • ????mPopWindow.showAsDropDown(mMenuTv);??
  • }??
  • 效果圖下:
    可見,點擊EditText沒有出現任何反應!所以如果PopupWindow沒有獲取焦點的能力,那么它其中的EditText當然是沒辦法獲取焦點的,EditText無法獲取焦點,那對它而言整個EditText控件就是不可用的。

    ?

    3、setOutsideTouchable(boolean touchable)

    這個函數的意義,就是指,PopupWindow以外的區域是否可點擊,即如果點擊PopupWindow以外的區域,PopupWindow是否會消失。
    下面這個是點擊會消息的效果圖:

    看看它對應的代碼:

    ?

    [java]?view plaincopy
  • private?void?showPopupWindow()?{??
  • ????View?contentView?=?LayoutInflater.from(MainActivity.this).inflate(R.layout.popuplayout,?null);??
  • ????mPopWindow?=?new?PopupWindow(contentView);??
  • ????mPopWindow.setWidth(ViewGroup.LayoutParams.FILL_PARENT);??
  • ????mPopWindow.setHeight(ViewGroup.LayoutParams.FILL_PARENT);??
  • ??
  • ????//外部是否可以點擊??
  • ????mPopWindow.setBackgroundDrawable(new?BitmapDrawable());??
  • ????mPopWindow.setOutsideTouchable(true);??
  • ??
  • ????…………//各ITEM點擊響應??
  • ??
  • ????mPopWindow.showAsDropDown(mMenuTv);??
  • ??
  • }??
  • 這里要非常注意的一點:
    [java]?view plaincopy
  • mPopWindow.setBackgroundDrawable(new?BitmapDrawable());??
  • mPopWindow.setOutsideTouchable(true);??
  • 大家可能要疑問,為什么要加上mPopWindow.setBackgroundDrawable(new BitmapDrawable());這句呢,從代碼來看沒并沒有真正設置Bitmap,而只是new了一個空的bitmap,好像并沒起到什么作用。那如果我們把這句去掉會怎樣:
    把代碼改成這樣子:(只使用setOutsideTouchable)
    [java]?view plaincopy
  • private?void?showPopupWindow()?{??
  • ????View?contentView?=?LayoutInflater.from(MainActivity.this).inflate(R.layout.popuplayout,?null);??
  • ????mPopWindow?=?new?PopupWindow(contentView);??
  • ????mPopWindow.setWidth(ViewGroup.LayoutParams.FILL_PARENT);??
  • ????mPopWindow.setHeight(ViewGroup.LayoutParams.FILL_PARENT);??
  • ??
  • ????//外部是否可以點擊??
  • ????mPopWindow.setOutsideTouchable(true);??
  • ??
  • ????…………//各ITEM點擊響應??
  • ??
  • ????mPopWindow.showAsDropDown(mMenuTv);??
  • }??

  • 看到了沒,點擊外部沒反應………………這就有點坑了,至于原因,我們在setBackgroundDrawable()中講。

    ?

    4、setBackgroundDrawable(Drawable background)

    這個函數可是吊了,這個函數不只能設置背景……,因為你加上它之后,setOutsideTouchable()才會生效;

    而且,只有加上它之后,PopupWindow才會對手機的返回按鈕有響應:即,點擊手機返回按鈕,可以關閉PopupWindow;如果不加setBackgroundDrawable()將關閉的PopupWindow所在的Activity.
    這個函數要怎么用,這里應該就不用講了吧,可以填充進去各種Drawable,比如new BitmapDrawable(),new ColorDrawable(),等;
    我們這里主要從源碼的角度來看看setBackgroundDrawable()后,PopupWindow都做了些什么。
    首先看看setBackgroundDrawable(),將傳進去的background賦值給mBackground;

    ?

    [java]?view plaincopy
  • void?setBackgroundDrawable(Drawable?background)?{??
  • ????mBackground?=?background;??
  • }??
  • 然后再看看顯示showAsDropDown()顯示的時候,都做了些什么。代碼如下:
    [java]?view plaincopy
  • public?void?showAsDropDown(View?anchor,?int?xoff,?int?yoff)?{??
  • ????…………??
  • ????//準備窗口??
  • ????WindowManager.LayoutParams?p?=?createPopupLayout(anchor.getWindowToken());??
  • ????preparePopup(p);??
  • ??
  • ????…………??
  • ????//顯示窗口??
  • ????invokePopup(p);??
  • }??
  • 在這段代碼中,先是準備窗口用來顯示,然后再利用invokePopup()來顯示窗體。
    我們看看在preparePopup(p)中是怎么準備窗體的:
    [html]?view plaincopy
  • private?void?preparePopup(WindowManager.LayoutParams?p)?{??
  • ????if?(mBackground?!=?null)?{??
  • ????????final?ViewGroup.LayoutParams?layoutParams?=?mContentView.getLayoutParams();??
  • ????????int?height?=?ViewGroup.LayoutParams.MATCH_PARENT;??
  • ????????if?(layoutParams?!=?null?&&??
  • ????????????????layoutParams.height?==?ViewGroup.LayoutParams.WRAP_CONTENT)?{??
  • ????????????height?=?ViewGroup.LayoutParams.WRAP_CONTENT;??
  • ????????}??
  • ??
  • ????????//?when?a?background?is?available,?we?embed?the?content?view??
  • ????????//?within?another?view?that?owns?the?background?drawable??
  • ????????PopupViewContainer?popupViewContainer?=?new?PopupViewContainer(mContext);??
  • ????????PopupViewContainer.LayoutParams?listParams?=?new?PopupViewContainer.LayoutParams(??
  • ????????????????ViewGroup.LayoutParams.MATCH_PARENT,?height??
  • ????????);??
  • ????????popupViewContainer.setBackgroundDrawable(mBackground);??
  • ????????popupViewContainer.addView(mContentView,?listParams);??
  • ??
  • ????????mPopupView?=?popupViewContainer;??
  • ????}?else?{??
  • ????????mPopupView?=?mContentView;??
  • ????}??
  • ????mPopupWidth?=?p.width;??
  • ????mPopupHeight?=?p.height;??
  • }??
  • 從上面可以看出,如果mBackground不這空,會首先生成一個PopupViewContainer的ViewContainer,然后把mContentView做為子布局add進去,然后把popupViewContainer做為PopupWindow做為根布局。
    [html]?view plaincopy
  • popupViewContainer.addView(mContentView,?listParams);??
  • 那如果mBackground不為空,那就直接把mContentView做為View傳遞給PopupWindow窗體。
    [java]?view plaincopy
  • mPopupView?=?mContentView??
  • 到此,我們知道,如果mBackground不為空,會在我們設置的contentView外再包一層布局。
    那下面,我們再看看包的這層布局都干了什么:
    先列出來完整的代碼,然后再分步講(已做精簡,如需知道更多,可參看源碼)

    ?

    ?

    [java]?view plaincopy
  • private?class?PopupViewContainer?extends?FrameLayout?{??
  • ????private?static?final?String?TAG?=?"PopupWindow.PopupViewContainer";??
  • ??
  • ????public?PopupViewContainer(Context?context)?{??
  • ????????super(context);??
  • ????}??
  • ????…………??
  • ???@Override??
  • ???public?boolean?dispatchKeyEvent(KeyEvent?event)?{??
  • ???????if?(event.getKeyCode()?==?KeyEvent.KEYCODE_BACK)?{??
  • ???????????if?(event.getAction()?==?KeyEvent.ACTION_DOWN??
  • ???????????????????&&?event.getRepeatCount()?==?0)?{??
  • ???????????????…………??
  • ???????????}?else?if?(event.getAction()?==?KeyEvent.ACTION_UP)?{??
  • ???????????????KeyEvent.DispatcherState?state?=?getKeyDispatcherState();??
  • ???????????????if?(state?!=?null?&&?state.isTracking(event)?&&?!event.isCanceled())?{??
  • ???????????????????dismiss();??
  • ???????????????????return?true;??
  • ???????????????}??
  • ???????????}??
  • ???????????return?super.dispatchKeyEvent(event);??
  • ???????}?else?{??
  • ???????????return?super.dispatchKeyEvent(event);??
  • ???????}??
  • ???}??
  • ??
  • ????@Override??
  • ????public?boolean?onTouchEvent(MotionEvent?event)?{??
  • ????????final?int?x?=?(int)?event.getX();??
  • ????????final?int?y?=?(int)?event.getY();??
  • ??????????
  • ????????if?((event.getAction()?==?MotionEvent.ACTION_DOWN)??
  • ????????????????&&?((x?<?0)?||?(x?>=?getWidth())?||?(y?<?0)?||?(y?>=?getHeight())))?{??
  • ????????????dismiss();??
  • ????????????return?true;??
  • ????????}?else?if?(event.getAction()?==?MotionEvent.ACTION_OUTSIDE)?{??
  • ????????????dismiss();??
  • ????????????return?true;??
  • ????????}?else?{??
  • ????????????return?super.onTouchEvent(event);??
  • ????????}??
  • ????}??
  • ????…………??
  • }??
  • 這里總共需要注意三部分:
    (1)、PopupViewContainer派生自FrameLayout
    從PopupViewContainer聲明上可以看到,PopupViewContainer派生自FrameLayout;所以,這也是它能將我們傳進來的contentView添加為自己的子布局的原因。
    (2)、返回按鈕捕捉
    [java]?view plaincopy
  • public?boolean?dispatchKeyEvent(KeyEvent?event)?{??
  • ???if?(event.getKeyCode()?==?KeyEvent.KEYCODE_BACK)?{??
  • ???????if?(event.getAction()?==?KeyEvent.ACTION_DOWN??
  • ???????????????&&?event.getRepeatCount()?==?0)?{??
  • ???????????…………??
  • ???????}?else?if?(event.getAction()?==?KeyEvent.ACTION_UP)?{??
  • ???????????//抬起手指時??
  • ???????????KeyEvent.DispatcherState?state?=?getKeyDispatcherState();??
  • ???????????if?(state?!=?null?&&?state.isTracking(event)?&&?!event.isCanceled())?{??
  • ???????????????//隱藏窗體??
  • ???????????????dismiss();??
  • ???????????????return?true;??
  • ???????????}??
  • ???????}??
  • ???????return?super.dispatchKeyEvent(event);??
  • ???}?else?{??
  • ???????return?super.dispatchKeyEvent(event);??
  • ???}??
  • }??
  • 從上面的代碼來看,PopupViewContainer捕捉了KeyEvent.KEYCODE_BACK事件,并且在用戶在點擊back按鈕,抬起手指的時候(event.getAction() == KeyEvent.ACTION_UP)將窗體隱藏掉。
    所以,添加上mBackground以后,可以在用戶點擊返回按鈕時,隱藏窗體!
    (3)、捕捉Touch事件——onTouchEvent
    [java]?view plaincopy
  • public?boolean?onTouchEvent(MotionEvent?event)?{??
  • ????final?int?x?=?(int)?event.getX();??
  • ????final?int?y?=?(int)?event.getY();??
  • ??????
  • ????if?((event.getAction()?==?MotionEvent.ACTION_DOWN)??
  • ????????????&&?((x?<?0)?||?(x?>=?getWidth())?||?(y?<?0)?||?(y?>=?getHeight())))?{??
  • ????????dismiss();??
  • ????????return?true;??
  • ????}?else?if?(event.getAction()?==?MotionEvent.ACTION_OUTSIDE)?{??
  • ????????dismiss();??
  • ????????return?true;??
  • ????}?else?{??
  • ????????return?super.onTouchEvent(event);??
  • ????}??
  • }??

  • 從這代碼來看,PopupViewContainer捕捉了兩種touch事件,MotionEvent.ACTION_DOWN和MotionEvent.ACTION_OUTSIDE;將接收到這兩個事件時,會將窗體隱藏掉。
    MotionEvent.ACTION_DOWN的觸發很好理解,即當用戶點擊到PopupViewContainer事件時,就隱藏掉;
    所以,下面的情況就來了:
    假如有一個TextView,我們沒有對它設置點擊響應。那只要加了background,那點擊事件就會傳給下層的PopupViewContainer,從而使窗體消失。
    那還有個問題,MotionEvent.ACTION_OUTSIDE是個什么鬼?它是怎么觸發的。我們在下面開一段細講。
    (4)MotionEvent.ACTION_OUTSIDE與setOutsideTouchable(boolean touchable)
    可能把這兩個放在一塊,大家可能就恍然大悟了,表著急,一個個來看。
    先看看setOutsideTouchable(boolean touchable)的代碼:

    ?

    ?

    [java]?view plaincopy
  • public?void?setOutsideTouchable(boolean?touchable)?{??
  • ???mOutsideTouchable?=?touchable;??
  • }??
  • 然后再看看mOutsideTouchable哪里會用到
    下面代碼,我做了嚴重精減,等下會再完整再講這一塊
    [java]?view plaincopy
  • private?int?computeFlags(int?curFlags)?{??
  • ????curFlags?&=?~(WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);??
  • ????…………??
  • ????if?(mOutsideTouchable)?{??
  • ????????curFlags?|=?WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;??
  • ????}??
  • ????…………??
  • ????return?curFlags;??
  • }??
  • 這段代碼主要是用各種變量來設置window所使用的flag;
    首先,將curFlags所在運算的各種Flag,全部置為False;代碼如下:
    [java]?view plaincopy
  • curFlags?&=?~(WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);??
  • 然后,再根據用戶設置的變量來開啟:
    [java]?view plaincopy
  • if?(mOutsideTouchable)?{??
  • ???curFlags?|=?WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;??
  • }??
  • 既然講到FLAG_WATCH_OUTSIDE_TOUCH,那我們來看看FLAG_WATCH_OUTSIDE_TOUCH所代表的意義:

    這段話的意思是說,如果窗體設置了FLAG_WATCH_OUTSIDE_TOUCH這個flag,那么 用戶點擊窗體以外的位置時,將會在窗體的MotionEvent中收到MotionEvetn.ACTION_OUTSIDE事件。
    參見:《WindowManager.LayoutParams》

    所以在PopupViewContainer中添加了對MotionEvent.ACTION_OUTSIDE的捕捉!當用戶點擊PopupViewContainer以外的區域時,將dismiss掉PopupWindow

    ?

    [java]?view plaincopy
  • public?boolean?onTouchEvent(MotionEvent?event)?{??
  • ????final?int?x?=?(int)?event.getX();??
  • ????final?int?y?=?(int)?event.getY();??
  • ??????
  • ????if?((event.getAction()?==?MotionEvent.ACTION_DOWN)??
  • ????????????&&?((x?<?0)?||?(x?>=?getWidth())?||?(y?<?0)?||?(y?>=?getHeight())))?{??
  • ????????dismiss();??
  • ????????return?true;??
  • ????}?else?if?(event.getAction()?==?MotionEvent.ACTION_OUTSIDE)?{??
  • ????????dismiss();??
  • ????????return?true;??
  • ????}?else?{??
  • ????????return?super.onTouchEvent(event);??
  • ????}??
  • }??
  • (5)重看PopupWindow的computeFlags(int curFlags)函數
    完整的computeFlags()函數如下:
    [java]?view plaincopy
  • private?int?computeFlags(int?curFlags)?{??
  • ????curFlags?&=?~(??
  • ????????????WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES?|??
  • ????????????WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE?|??
  • ????????????WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE?|??
  • ????????????WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH?|??
  • ????????????WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS?|??
  • ????????????WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM?|??
  • ????????????WindowManager.LayoutParams.FLAG_SPLIT_TOUCH);??
  • ????if(mIgnoreCheekPress)?{??
  • ????????curFlags?|=?WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;??
  • ????}??
  • ????if?(!mFocusable)?{??
  • ????????curFlags?|=?WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;??
  • ????????if?(mInputMethodMode?==?INPUT_METHOD_NEEDED)?{??
  • ????????????curFlags?|=?WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;??
  • ????????}??
  • ????}?else?if?(mInputMethodMode?==?INPUT_METHOD_NOT_NEEDED)?{??
  • ????????curFlags?|=?WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;??
  • ????}??
  • ????if?(!mTouchable)?{??
  • ????????curFlags?|=?WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;??
  • ????}??
  • ????if?(mOutsideTouchable)?{??
  • ????????curFlags?|=?WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;??
  • ????}??
  • ????if?(!mClippingEnabled)?{??
  • ????????curFlags?|=?WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;??
  • ????}??
  • ????if?(isSplitTouchEnabled())?{??
  • ????????curFlags?|=?WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;??
  • ????}??
  • ????if?(mLayoutInScreen)?{??
  • ????????curFlags?|=?WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;??
  • ????}??
  • ????if?(mLayoutInsetDecor)?{??
  • ????????curFlags?|=?WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;??
  • ????}??
  • ????if?(mNotTouchModal)?{??
  • ????????curFlags?|=?WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;??
  • ????}??
  • ????return?curFlags;??
  • }??
  • 這段代碼同樣是分兩段:
    第一段:將所有要計算的FLAG,全部在結果curFlags中置為FALSE;
    [java]?view plaincopy
  • curFlags?&=?~(??
  • ????????WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES?|??
  • ????????WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE?|??
  • ????????WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE?|??
  • ????????WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH?|??
  • ????????WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS?|??
  • ????????WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM?|??
  • ????????WindowManager.LayoutParams.FLAG_SPLIT_TOUCH);??
  • 第二段:然后根據用戶設置的變量,逐個判斷是否打開。比如下面這個:
    [java]?view plaincopy
  • //看到了吧,我們的setTouchable(boolean?touchable)最終也是通過在這里設置的??
  • if?(!mTouchable)?{??
  • ????curFlags?|=?WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;??
  • }??
  • 好了,結合源碼就給大家講到這里了。最后總結一下:
    **如果我們給PopupWindow添加了mBackground,那它將會:**
    • setOutsideTouchable(true)將生效,具有外部點擊隱藏窗體的功能
    • 手機上的返回鍵將可以使窗體消失
    • 對于PopupWindow上層沒有捕捉的點擊事件,點擊之后,仍然能使窗體消失。
    源碼在文章底部給出

    二、為什么要強制代碼設置PopupWindow的Height、Width

    ?

    在上篇,我們留了這么個疑問,設置contentView很容易理解,但width和height為什么要強制設置呢?我們在布局代碼中不是已經寫的很清楚了么?比如我們的popuplayout.xml的根布局:

    ?

    [html]?view plaincopy
  • <RelativeLayout??
  • ????????xmlns:android="http://schemas.android.com/apk/res/android"??
  • ????????android:layout_width="fill_parent"??
  • ????????android:layout_height="fill_parent"??
  • ????????android:background="#66000000">??
  • ????????…………??
  • </RelativeLayout>?????
  • 從根布局中,我們明明可以看到layout_width我們設置為了"fill_parent",layout_height設置為了“fill_parent”;為什么非要我們在代碼中還要再設置一遍:
    [java]?view plaincopy
  • View?contentView?=?LayoutInflater.from(MainActivity.this).inflate(R.layout.popuplayout,?null);??
  • mPopWindow?=?new?PopupWindow(contentView);??
  • mPopWindow.setWidth(ViewGroup.LayoutParams.FILL_PARENT);??
  • mPopWindow.setHeight(ViewGroup.LayoutParams.FILL_PARENT);??
  • 帶著這個疑問,我們從兩個角度來分析,”源碼角度”看看就好,關鍵的解答在第二部分:布局角度;

    1、源碼角度

    首先,我們從源碼我角度來分析為什么要設置Width和Height;我們就以setWidth()為例來追根尋底下
    先看下setWidth():
    [java]?view plaincopy
  • public?void?setWidth(int?width)?{??
  • ????mWidth?=?width;??
  • }??
  • 然后再看看mWidth在哪里用到:
    [java]?view plaincopy
  • private?WindowManager.LayoutParams?createPopupLayout(IBinder?token)?{??
  • ????//?generates?the?layout?parameters?for?the?drop?down??
  • ????//?we?want?a?fixed?size?view?located?at?the?bottom?left?of?the?anchor??
  • ????WindowManager.LayoutParams?p?=?new?WindowManager.LayoutParams();??
  • ????//?these?gravity?settings?put?the?view?at?the?top?left?corner?of?the??
  • ????//?screen.?The?view?is?then?positioned?to?the?appropriate?location??
  • ????//?by?setting?the?x?and?y?offsets?to?match?the?anchor's?bottom??
  • ????//?left?corner??
  • ????p.gravity?=?Gravity.LEFT?|?Gravity.TOP;??
  • ????p.width?=?mLastWidth?=?mWidth;??
  • ????p.height?=?mLastHeight?=?mHeight;??
  • ????if?(mBackground?!=?null)?{??
  • ????????p.format?=?mBackground.getOpacity();??
  • ????}?else?{??
  • ????????p.format?=?PixelFormat.TRANSLUCENT;??
  • ????}??
  • ????p.flags?=?computeFlags(p.flags);??
  • ????p.type?=?mWindowLayoutType;??
  • ????p.token?=?token;??
  • ????p.softInputMode?=?mSoftInputMode;??
  • ????p.setTitle("PopupWindow:"?+?Integer.toHexString(hashCode()));??
  • ??
  • ????return?p;??
  • }??
  • 上面是createPopupLayout的完整代碼,我們提取一下:
    [java]?view plaincopy
  • private?WindowManager.LayoutParams?createPopupLayout(IBinder?token)?{??
  • ????…………??
  • ????p.width?=?mLastWidth?=?mWidth;??
  • ????p.height?=?mLastHeight?=?mHeight;??
  • ????…………??
  • ????return?p;??
  • }??
  • 從這里便可以清晰的看到窗體的寬度和高度都是通過mWidth和mHeight來設置的。
    那問題來了,mWidth在哪里能設置呢:
    通篇代碼中,總共只有三個函數能設置mWidth,分別如下:
    除了setWidth()函數本身,就只有PopupWindow()的兩個構造函數了;
    [java]?view plaincopy
  • public?void?setWidth(int?width)?{??
  • ????mWidth?=?width;??
  • }??
  • public?PopupWindow(View?contentView,?int?width,?int?height)?{??
  • ????this(contentView,?width,?height,?false);??
  • }??
  • public?PopupWindow(View?contentView,?int?width,?int?height,?boolean?focusable)?{??
  • ???if?(contentView?!=?null)?{??
  • ???????mContext?=?contentView.getContext();??
  • ???????mWindowManager?=?(WindowManager)?mContext.getSystemService(Context.WINDOW_SERVICE);??
  • ???}??
  • ???setContentView(contentView);??
  • ???setWidth(width);??
  • ???setHeight(height);??
  • ???setFocusable(focusable);??
  • }??
  • 那么問題來了,如果我們沒有設置width和height那結果會如何呢?
    如果我們沒有設置width和height,那mWidth和mHeight將會取默認值0!!!!所以當我們沒有設置width和height時,并不是我們的窗體沒有彈出來,而是因為他們的width和height都是0了!!!!
    **那么問題又來了:Google那幫老頭,不能從我們contentView的根布局中取參數嗎,非要我們自己設?**
    當然不是那幫老頭的代碼有問題,因為這牽涉了更深層次的內容:布局參數的設定問題!我們在下一部分,布局角度來解答。

    2、布局角度

    這部分我們著重講一個問題:控件的布局參數從哪里來?
    我們看下面這段XML:

    ?

    ?

    [html]?view plaincopy
  • <?xml?version="1.0"?encoding="utf-8"?>??
  • <RelativeLayout??
  • ????????xmlns:android="http://schemas.android.com/apk/res/android"??
  • ????????android:layout_width="fill_parent"??
  • ????????android:layout_height="fill_parent">??
  • ????<LinearLayout??
  • ????????????android:layout_width="match_parent"??
  • ????????????android:layout_height="wrap_content"??
  • ????????????android:background="@drawable/pop_bg"??
  • ????????????android:orientation="vertical"??
  • ????????????android:paddingBottom="2dp"??
  • ????????????android:layout_alignParentRight="true">??
  • ??
  • ????????<TextView??
  • ????????????????android:id="@+id/pop_computer"??
  • ????????????????android:layout_width="wrap_content"??
  • ????????????????android:layout_height="wrap_content"??
  • ????????????????style="@style/pop_text_style"??
  • ????????????????android:text="計算機"/>??
  • ????</LinearLayout>??
  • </RelativeLayout>??
  • 很明顯,這段代碼是個三層結構,TextView是最終的子控件。
    那我現在要問了:TextView的顯示大小是由誰來決定的?
    是由它自己的布局layout_width="wrap_content"、layout_height="wrap_content"來決定的嗎?
    當然不是!!!!它的大小,應該是在它父控件的基礎上決定的。即LinearLayout的顯示大小確定了以后,才能確定TextView的大小。
    這好比,如果LinearLayout的大小是全屏的,那TextView的大小就由它自己來決定了,那如果LinearLayout的大小只有一像素呢?那TextView的所顯示的大小無論它自己怎么設置,最大也就顯示一像素!
    所以我們的結論來了:控件的大小,是建立在父控件大小確定的基礎上的。
    那同樣:LinearLayout的大小確定是要靠RelativeLayout來決定。
    那問題來了:RelativeLayout的大小靠誰決定呢?
    當然是它的父控件了。
    我們以前講過ViewTree的概念,即在android中任何一個APP都會有一個根結點,然后它所有的Activity和Fragmentr所對應的布局都會加入到這個ViewTree中;在ViewTree中每一個控件是一個結點:
    比如下面這個ViewTree(畫的很爛……)

    ?

    從上面的ViewTree中可以看到,每一個結點都是有父結點的(除了根結點,根結點不是應用的根結點,與我們應用無關),所以每一個控件都是可以找到父控件的的布局大小的。
    但我們的contentView是怎么來的呢?

    ?

    [java]?view plaincopy
  • View?contentView?=?LayoutInflater.from(MainActivity.this).inflate(R.layout.popuplayout,?null);??
  • 直接inflate出來的,我們對它沒有設置根結點!
    那問題來了?它的大小由誰來解決呢?
    好像沒有誰能決定了,因為他沒有父結點。那它到底是多大呢?未知!
    所以只有通過代碼讓用戶去手動設置了!所以這就是為什么非要用戶設置width和height的原因了。

    好了,到這里,有關PopupWIndow的東東也就講完了,希望大家能學到東西。

    ?

    如果本文有幫到你,記得加關注哦

    源碼下載地址:http://download.csdn.net/detail/harvic880925/9197073

    請大家尊重原創者版權,轉載請標明出處:http://blog.csdn.net/harvic880925/article/details/49278705?謝謝

    ?

    轉載于:https://www.cnblogs.com/xgjblog/p/7688153.html

    總結

    以上是生活随笔為你收集整理的PopUpWindow使用详解(二)——进阶及答疑的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。