前言
最近在項(xiàng)目中用到了 PopUpWindow砸脊,并且在機(jī)型適配時(shí)發(fā)現(xiàn)華為等具有虛擬按鍵的手機(jī)在橫屏狀態(tài)時(shí)會(huì)造成 PopUpWindow 顯示位置偏移的情況存在畜隶,最后完美解決了這個(gè)問題敬矩,所以把經(jīng)驗(yàn)分享出來嚼黔,看能否對(duì)你有用引有。
彈窗有很多種實(shí)現(xiàn)方式瓣颅,例如:
- Dialog
- DialogFragment
- Fragment
- PopUpWindow
- ListPopUpWindow
在這些方式中,我們主要講 PopUpWindow 的使用方式譬正,因?yàn)樗茉谌我馕恢脧棾觯?這是其他方式很難做到的宫补。
PopUpWindow 是什么
從 Google 爸爸的開發(fā)文檔中我們不難看出,首先它是一個(gè) Window曾我,彈出時(shí)位于其他控件的上層粉怕。
怎么使用 PopUpWindow
- 創(chuàng)建布局文件
- 創(chuàng)建 ContentView
- 設(shè)置 PopUpWindow
- Show
創(chuàng)建布局文件
PopUpWindow 就是一個(gè)容器,是需要編寫對(duì)應(yīng)的布局文件抒巢,在項(xiàng)目需求中我們要做成這樣的效果
<com.tutu.app.view.TutuRegisterTitleView xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:background="@color/sdk_background"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tutu_game_register_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:layout_marginTop="4dp"
android:textAlignment="center"
android:layout_marginBottom="6dp"
android:textColor="#333333"
android:textSize="17dp" />
<View
style="@style/TutuGameFullLandscapeLine"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"/>
</com.tutu.app.view.TutuRegisterTitleView>
創(chuàng)建 ContentView
ContentView 是我們將布局文件生成的View贫贝,并且將其放入 PopUpWindow 中。
// 初始化popUpWindow
linearLayout = new LinearLayout(getContext());
// 生成 View 對(duì)象
popRootView = View.inflate(getContext(),
// PopUpWindow 傳入 ContentView
popupWindow = new PopupWindow(popRootView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
設(shè)置 PopUpWindow
看到這里蛉谜,有人可能問:我們?cè)诓季治募幸呀?jīng)設(shè)置了背景顏色稚晚,為什么要在Java代碼中重復(fù)設(shè)置呢?
因?yàn)樵谀承?Andorid 系統(tǒng)版本中會(huì)出現(xiàn)點(diǎn)擊外部和返回鍵彈窗卻無法消失的 Bug型诚,所以你懂的互亮。
附上某大牛對(duì)該 Bug 的分析 PopupWindow 點(diǎn)擊外部和返回鍵無法消失背后的真相
// 設(shè)置背景
popupWindow.setBackgroundDrawable(new ColorDrawable());
// 外部點(diǎn)擊事件
popupWindow.setOutsideTouchable(true);
Show
這個(gè)就很簡(jiǎn)單摔笤,短短一行代碼,控制彈窗的顯示實(shí)際位置,難點(diǎn)在于 x敢茁,y 值的確定
// 傳入 AnchorView 绷柒,錨點(diǎn)實(shí)際為 Window
// Gravity.TOP 在該錨點(diǎn)的正上方
// Gravity.LEFT 在屏幕左側(cè)
// Gravity.NO_GRAVITY,在屏幕左上角
// x 為坐標(biāo)系 x軸方向的偏移量哈街,左負(fù)右正
// y 為坐標(biāo)系 y軸方向的偏移量蛉签,上負(fù)下正
popupWindow.showAtLocation(view, Gravity.TOP, 0, y);
popupWindow.showAtLocation(view, Gravity.NO_GRAVITY,x, y);
popupWindow.showAtLocation(view, Gravity.TOP, 0, y);
怎樣使用 ListPopUpWindow
和PopUpWindow 相比,它更適合展示多條數(shù)據(jù)摸柄,內(nèi)部包含了一個(gè) ListView 颤练,那就意味著需要 Adapter 進(jìn)行數(shù)據(jù)的綁定。
listPopupWindow = new ListPopupWindow(getContext());
// 適配器添加數(shù)據(jù)
listPopupWindowAdapter.addAdapterList(list);
// 添加適配器
listPopupWindow.setAdapter(listPopupWindowAdapter);
// 設(shè)置彈窗的大小和位置
listPopupWindow.setWidth(width+horizontalOffset*2);
listPopupWindow.setHeight(height);
listPopupWindow.setAnchorView(view);
listPopupWindow.setModal(true);
listPopupWindow.setVerticalOffset(-VerticalOffset);
listPopupWindow.setHorizontalOffset(-horizontalOffset*4);
// 設(shè)置背景
listPopupWindow.setBackgroundDrawable(
BitmapUtils.getDrawableFromResource(getContext(), RUtils.drawable(getContext(),"tutu_area_code_background")));
虛擬按鍵對(duì) PopUpWindow 的影響
虛擬按鍵的機(jī)型在橫屏狀態(tài)下塘幅,會(huì)造成一個(gè) x 軸方向的偏移(根據(jù)具體代碼確定)昔案,所以我們使用神器 getLocationInWindow尿贫,獲取錨點(diǎn) View 在當(dāng)前 Window 的坐標(biāo),然后通過計(jì)算確定彈窗出現(xiàn)位置踏揣。
具體決定方案庆亡,請(qǐng)看 Fucking Code
// 獲取錨點(diǎn) View 在屏幕中的坐標(biāo)
int[] location = new int[2];
back.getLocationInWindow(location);
int x = location[0];//獲取當(dāng)前位置的橫坐標(biāo)
int y = location[1];//獲取當(dāng)前位置的縱坐標(biāo)
// 豎屏不做處理
if (VERTICAL_SCREEN == getContext().getResources().getConfiguration().orientation){
popupWindow.showAtLocation(view, Gravity.TOP, 0, y);
}
// 橫屏狀態(tài)
else if (HORIZONTALL_SCREEN == getContext().getResources().getConfiguration().orientation) {
// 檢測(cè)是否有虛擬按鍵
if (checkDeviceHasNavigationBar(getContext())){
popupWindow.showAtLocation(view, Gravity.NO_GRAVITY,x, y);
}else {
popupWindow.showAtLocation(view, Gravity.TOP, 0, y);
}
}