前言
??在某些情況下洗贰,我們需要一進(jìn)入Activity就顯示PopupWindow陨倡,比如常見的選擇界面敛滋。但由于PopupWindow是依附于Activity的兴革,如果Activity沒有創(chuàng)建完成,Activity還沒完全顯示出來就顯示PopupWindow的話庶艾,會(huì)出現(xiàn)異城婵保現(xiàn)象。
問題復(fù)現(xiàn)
??我在Activity的onCreate()方法中調(diào)用如下方法:
public void show( ){
if( null != mPopupWindow ){
mPopupWindow.showAtLocation(mView, Gravity.CENTER, 0, 0);
}
}
??運(yùn)行程序的時(shí)候出現(xiàn)如下異常:
解決方案
??在StackOverflow上搜索這個(gè)問題你會(huì)發(fā)現(xiàn)煤裙,都沒有原因分析噪漾,但我在《Android開發(fā)精要》一書中找到了答案(P158):
??PopupWindow不像對(duì)話框那樣從屏幕的固定位置彈出,而是依賴于錨點(diǎn)控件對(duì)象的位置欣硼,所謂錨點(diǎn)控件對(duì)象,就是界面組件中的某個(gè)控件遍愿,PopupWindow的展示和功能都是以它為核心耘斩,作為錨點(diǎn)控件的擴(kuò)展交互界面,以增強(qiáng)該控件對(duì)象的功能括授。
??彈出窗口與描點(diǎn)控件有著緊密的聯(lián)系岩饼,在構(gòu)造并展示彈出窗口前薛夜,需要保證錨點(diǎn)控件所在的控件樹已經(jīng)與窗口管理服務(wù)建立連接,因?yàn)樵趶棾龃翱诘恼故具^程中寞冯,需要通過該窗口對(duì)象來獲取相關(guān)信息晚伙。在界面組件的構(gòu)造過程中,窗口連接的建立是個(gè)異步過程咆疗,也就是說,當(dāng)Activity.onCreate()等函數(shù)被調(diào)用時(shí)尝抖,界面與窗口管理服務(wù)的雙向通信連接尚未建立迅皇,如果在此時(shí)構(gòu)造彈出窗口則會(huì)拋出異常。因此喧半,如果期望在界面組件展現(xiàn)之處便構(gòu)造彈出窗口青责,可以將彈出窗口對(duì)象構(gòu)造也轉(zhuǎn)換成一個(gè)異步過程。
// 在界面組件onCreate函數(shù)中調(diào)用 final View anchor = findViewById(R.id.anchor); anchor.post(new Runnable(){ @Override public void run(){ // 構(gòu)造和展現(xiàn)彈出窗口 PopupWindow window = createWindow(); window.showAsDropDown(anchor); } });
??在與窗口管理服務(wù)未建立連接之前扁耐,界面組件將通過View.post函數(shù)發(fā)送過來的消息放入一個(gè)靜態(tài)隊(duì)列當(dāng)中产阱,在通信連接建立完成后,再從該隊(duì)列中讀取消息并一一執(zhí)行王暗。因此,通過這樣的實(shí)現(xiàn)模型可以保證彈出窗口展現(xiàn)時(shí)窗口通信連接已經(jīng)構(gòu)建成功俗壹。
??所以對(duì)于上面的問題藻烤,最簡(jiǎn)單的處理方法是头滔,異步顯示PopupWindow就好了:
public void show( ){
mView.post( new Runnable( ) {
@Override
public void run() {
if( null != mPopupWindow ){
mPopupWindow.showAtLocation(mView, Gravity.CENTER, 0, 0);
}
}
});
}