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