最詳細的 PopupWindow 詳解
我看網上對于PopupWindow的介紹非常的少就自己寫一篇, 本文基本上分析了PopupWindow的所有方法.
PopupWindow是對于屏幕添加一個顯示區域, 由于對位置和內容都非常自由所以常常在開發中用到.
看完后建議也看下PopupMenu詳細使用
創建
一般用的構造方法.
PopupWindow () // 創建一個空的PopupWindow
PopupWindow (View contentView)
PopupWindow (int width,
int height)
PopupWindow (View contentView, // PopupWindow的內容View, 相當于setContentView
int width, // 寬, 相當于setwidth()
int height, // 高, 相當于setHeight
boolean focusable) // 是否可獲取焦點, 相當于setFocusable()
通過上下文創建PopupWindow, 創建后默認有一個透明的背景.默認寬高(0,0), 沒有內容和焦點的PopupWindow. 具體作用我也不知道, 估計是寫自定義控件的吧. 但是PopupWindow并沒有繼承View.一般不使用該構造.
PopupWindow (Context context)
PopupWindow (Context context,
AttributeSet attrs)
PopupWindow (Context context,
AttributeSet attrs,
int defStyleAttr)
PopupWindow (Context context,
AttributeSet attrs,
int defStyleAttr,
int defStyleRes)
創建PopuWindow必要的三個條件:
void setHeight (int height) // 因為PopupWindow沒有默認布局所以必須指定寬高
void setWidth (int width)
void setContentView (View contentView) // 需要顯示的內容
缺少一個就無法顯示.
前面提到PopupWindow需要設置寬高, 那如果想用布局中的寬高怎么辦呢?
可以用到 LayoutParams.WRAP_CONTENT 包裹布局. 布局多大就顯示多大的PopupWindow
PopupWindow popupWindow = new PopupWindow(popupView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
顯示
顯示PopupWindow可以分為兩種方式:
- 附著某個控件 showAsDropDown
- 設置屏幕坐標 showAtLocation
相對于當前控件
默認是PopupWindow的左上角對其控件的左下角
或者設置 Gravity.RIGHT , PopupWindow的右上角對齊控件的右下角
不存在 Gravity.TOP 或 Gravity.BOTTOM 效果
void showAsDropDown (View anchor) // 彈窗顯示在anchor控件左下方
void showAsDropDown (View anchor,
int xoff, // 以控件左下角為原點的偏移坐標
int yoff)
void showAsDropDown (View anchor,
int xoff,
int yoff,
int gravity)
// 彈窗顯示在控件的左下方還是右下方, 參數Gravity.RIGHT/Gravity.LEFT. 默認是左下方
相對于當前窗口
當前窗口的任意位置(不包括狀態欄)
void showAtLocation (View parent, // 該屬性只要是屏幕上任意控件對象即可
int gravity, // 屏幕位置
int x, // 偏移坐標
int y)
parent:該屬性只要是當前任意控件對象即可(View和ViewGroup都行), 官方文檔介紹該對象參數主要是為了得到該對象的 getWindowToken() 方法.
需要注意的是多次調用show方法, 只會執行第一句
mPopupWindow.showAtLocation(popupwindow, Gravity.TOP, 100, 0); // 只有該行生效
mPopupWindow.showAtLocation(popupwindow, Gravity.LEFT, 100, 0);
mPopupWindow.showAtLocation(popupwindow, Gravity.RIGHT, 100, 0);
mPopupWindow.showAtLocation(popupwindow, Gravity.BOTTOM, 100, 0);
隱藏PopupWindow
該方法只能在 show 后才能執行否則crash
void dismiss ()
狀態
可被點擊
boolean isTouchable () // 判斷是否可被點擊
void setTouchable (boolean touchable) // 設置是否可被點擊
多點觸控
void setSplitTouchEnabled (boolean enabled)
boolean isSplitTouchEnabled ()
忽略CheekPress事件
當物體觸摸在屏幕上的尺寸超過手指尺寸范圍, 將被判定為CheekPress事件(臉頰點擊).
void setIgnoreCheekPress () // 默認為false, 即不忽略
彈窗外部被點擊
如果為true點擊PopupWindow外部區域可以取消PopupWindow
void setOutsideTouchable (boolean touchable) // 設置外部是否可被點擊
boolean isOutsideTouchable ()
解決NavigationBar重疊
這是Android5.0(API22)后添加的方法, 默認為true. 為true時將不會與導航欄重疊.
void setAttachedInDecor (boolean enabled)
可獲取焦點
一般控件都不需要焦點. 但是輸入框EditText需要先獲取焦點才能輸入. 最重要的是當PopupWindow可獲取焦點時按下手機返回鍵將不會銷毀當前Activity而是關閉當前PopupWindow. 所以我們一般還是設置為true. 更加符合用戶操作邏輯. 該方法為true時同時擁有 setOutsideTouchable(true) 的作用.
void setFocusable (boolean focusable)
boolean isFocusable ()
設置背景
void setBackgroundDrawable (Drawable background)
Drawable getBackground ()
陰影
該方法我測試無效
void setElevation (float elevation)
float getElevation ()
附著View位置
該方法只在 showAsDropDown() 方法執行后才有效. 可以判斷PopupWindow和附著View anchor誰的Y軸坐標小.
boolean isAboveAnchor ()
遮蓋附著View
void setOverlapAnchor (boolean overlapAnchor)
boolean getOverlapAnchor ()
可以從圖中看到對齊方式從View anchor的左下角變成了左上角了.
設置PopupWindow寬高
該方法在API23后被廢棄, 由setWidth(int) 和 setHeight(int)替代
void setWindowLayoutMode (int widthSpec,
int heightSpec)
窗口裁剪
PopupWindow默認是不會超出屏幕邊界的. 但是如果該方法為false時會采用精準位置, 能超出屏幕范圍.
void setClippingEnabled (boolean enabled)
boolean isClippingEnabled ()
演示超出屏幕:
mPopupWindow.showAtLocation(mBtnOpenPopup, Gravity.BOTTOM, 0, -30);
動畫效果
設置動畫
可以設置popupWindow的顯示和隱藏動畫
void setAnimationStyle (int animationStyle)
int getAnimationStyle ()
可以看到方法是傳入一個Style的樣式id
示例:
<style name="popupwindow_anim_style">
<item name="android:windowEnterAnimation">@anim/dialog_bottom_enter</item>
<item name="android:windowExitAnimation">@anim/dialog_bottom_exit</item>
</style>
分別由兩個屬性組成. 兩個屬性各代表一個anim動畫文件.
進入和退出動畫
這是在Android6.0(API 23)后加入的方法. 配合Material Design的轉場動畫使用.
進入動畫
void setEnterTransition (Transition enterTransition)
Transition getEnterTransition ()
退出動畫
void setExitTransition (Transition exitTransition)
Transition getExitTransition ()
獲取
獲取最大高度
這是相當于傳入的View對象可顯示的最大高度. 即PopupWindow使用 showAsDropDown() 能夠顯示的最大高度
int getMaxAvailableHeight (View anchor)
int getMaxAvailableHeight (View anchor,
int yOffset) // 控件Y軸偏移后可顯示的最大高度
int getMaxAvailableHeight (View anchor, // api24增加的方法, 由于我手上沒有7.0設備就不說了.
int yOffset,
boolean ignoreBottomDecorations)
輸入模式
針對PopupWindow中包含EditText控件.
輸入模式
我使用該方法的三種模式我感覺并沒有什么卵用
void setInputMethodMode (int mode)
int getInputMethodMode ()
支持三種模式
- INPUT_METHOD_FROM_FOCUSABLE 根據可否獲取焦點判斷是否可輸入. 感覺雞肋
- INPUT_METHOD_NEEDED 允許輸入
- INPUT_METHOD_NOT_NEEDED 不允許輸入
軟鍵盤模式
void setSoftInputMode (int mode) // mode為WindowManager.LayoutParams的softInputMode常量
int getSoftInputMode ()
softInputMode
包含九種取值, 可組合使用,分為兩類:
顯示狀態模式
- SOFT_INPUT_STATE_UNSPECIFIED 默認模式
- SOFT_INPUT_STATE_HIDDEN
- SOFT_INPUT_STATE_ALWAYS_HIDDEN 總是隱藏
- SOFT_INPUT_STATE_UNCHANGED
- SOFT_INPUT_STATE_VISIBLE
- SOFT_INPUT_STATE_ALWAYS_VISIBLE 自動彈出軟鍵盤
調整模式
- SOFT_INPUT_ADJUST_UNSPECIFIED 默認模式
- SOFT_INPUT_ADJUST_RESIZE 軟鍵盤彈出后PopupWindow會自動調整坐標,不被遮擋
- SOFT_INPUT_ADJUST_PAN
監聽事件
隱藏事件監聽
即PopupWindow執行 dismiss() 后回調的方法.
void setOnDismissListener (PopupWindow.OnDismissListener onDismissListener)
觸摸事件攔截
void setTouchInterceptor (View.OnTouchListener l)
更新
以下的更新PopupWindow都必須在PopupWindow處于以及被顯示的狀態下才行. 且PopupWindow的寬高設置都必須大于等于0. 如果想忽略PopupWindow的寬高設置就設為-1.
更新狀態
該方法不能更新PopupWindow的寬高, 只能更新PopupWindow的狀態. 例如更新 Focusable 和 OutsideTouchable
void update ()
更新尺寸
上面說過 update() 不能更新PopupWindow的寬高, 但是提供更新寬高的update方法
void update (int width, // 更新PopupWindow的寬高
int height)
更新顯示位置
該方法是相當于重新 showAsDropDown , 所以這是相對于控件的位置更新
void update (View anchor, // 更新顯示控件的位置
int width,
int height)
void update (View anchor,
int xoff, // 相對于控件的偏移值
int yoff,
int width,
int height)
相對位置更新
是相對于當前的位置進行偏移. 不同的顯示位置對于的相對原點也不同.
showAsDropDown 的相對原點是整個屏幕左上角, 包括狀態欄. 所以由于包括狀態欄所以坐標偏移的時候一定要y軸偏移大于60超出狀態欄的高度. 否則因為遮擋狀態欄導致PopupWindow無法顯示.
mPopupWindow.update(50, 60, -1,-1); // x軸偏移50
showAtLocation 的相對原點是自身位置.
void update (int x, // 坐標偏移
int y,
int width, // PopupWindow寬高
int height)
void update (int x,
int y,
int width,
int height,
boolean force) // 可獲取焦點
來自:https://juejin.im/post/58ed82c3a22b9d0063469e98