1. 程式人生 > >全面解析PopupWindow不消失,不彈框等各種問題

全面解析PopupWindow不消失,不彈框等各種問題

關於寫這篇文章的目的是因為專案中要寫一個PopupWindow的彈出框動畫,結果碰到各種坑,各種百度,但是說的要麼支離破碎,要麼根本就是錯的,所以再次整合了一下,併力所能及的解決了一些問題。

大部分人都碰到了PopupWindow點選外部不會消失或者點選沒反應等等的情況,然而百度上解決的方法就那麼幾種可能你寫完之後發現還是沒什麼用,其實並不是因為他們說錯了,而是你建立pop時的設定和他們根本不同導致的問題,下面我詳細的說明下各種建立方法會導致的問題已經解決方法。

其實大部分能不能關閉,能不能點選都是setFocusable是true還是false導致的,那麼我將對這2種狀態進行分析。

在mPopupWindow.setFocusable(true)時的情況

第一種情況:

new PopupWindow(contentView, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
如果這裡的高度是WRAP_CONTENT(xml中的佈局也到不滿屏的地步)
1.1那麼點選PopupWindow外部 PopupWindow是會自動dismiss的並且activity的onTouchEvent和dispatchTouchEvent都不會觸發(他會優先觸發dismiss)  換句話說你想要在PopupWindow顯示的情況下點選外部的一些控制元件就很難了,(解決方法下面再說)
1.2點選返回鍵PopupWindow也是會自動dismiss的  並且activity中的onKeyDown(int keyCode, KeyEvent event)也無法觸發 換句話說你想要在PopupWindow顯示的情況下控制返回鍵也已經不可能了
1.3點選PopupWindow中的EditText可以彈出鍵盤
1.4在安卓7.0以上的手機上這個顯示也是正確的(會從指定的控制元件下方彈出)

第二種情況:

new PopupWindow(contentView, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
如果這裡的高度是MATCH_PARENT
2.1那麼PopupWindow自然是不存在什麼外部空閒區域了 1.1的情況也就不存在了其他都一樣  想讓它dismiss 只能按返回鍵或者自己呼叫他的dismiss方法。

PS:在這裡宣告一下 網上說的那些
1.PopupWindow.showAtLocation(mThreeTest, Gravity.TOP, 0, 500)MATCH_PARENT 這裡的500並沒有什麼用 頂部的500依然算內部
2.PopupWindow.showAsDropDown(mThreeTest, 0, 0)  這裡說明下  如果用了這個那mThreeTest的控制元件已經算外部了
3.PopupWindow.setBackgroundDrawable(new BitmapDrawable());
4.contentView.setFocusable(true); contentView.setFocusableInTouchMode(true); 
這都親自在各種情況下一一試過  反正得到的結果都是一樣的 只要setFocusable(true)都是以上寫的幾個結論。


在mPopupWindow.setFocusable(false)時的情況

第一種情況:

new PopupWindow(contentView, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
1.1那麼點選PopupWindow外部 PopupWindow並不會dismiss activity的onTouchEvent和dispatchTouchEvent也都能接受 總之PopupWindow以外的控制元件點選都是有效的
1.2點選返回鍵的話和沒有PopupWindow是一樣的 原本是什麼邏輯就是什麼邏輯  而且activity中的onKeyDown(int keyCode, KeyEvent event)也是能監聽到的
1.3點選PopupWindow中的EditText不可以彈出鍵盤 (點選textView等控制元件是有反應的只是EditText的鍵盤不彈而已並不是內部控制元件點選無效)
1.4在安卓7.0版本以上顯示的位置不正確,總會在螢幕最上方顯示(解決方法下面說明)

第二種情況:

new PopupWindow(contentView, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
 如果這裡的高度是MATCH_PARENT
2.1PopupWindow也不存在什麼外部空閒區域了  點選類似於外部的控制元件也是無任何反應的

這個的2.1我在說一遍 如果PopupWindow.showAsDropDown(mThreeTest, 0, 0)  那麼即使是MATCH_PARENT  mThreeTest依然算外部  既然是外部當然是有反應的


以上就是PopupWindow在各種設定下會出現的情況  那麼上面還留了幾個問題  在回答這些問題前我們先總結下
如果想PopupWindow 實現點選外部就消失 就把setFocusable(true)就可以了  缺點:當然在顯示時 返回鍵也就必須是消失它了
如果想PopupWindow 顯示的情況下還能點選外部的控制元件 那麼setFocusable(fasle)就可以了 缺點:點選PopupWindow中的editView 鍵盤不會彈出了 並且在7.0以上的系統顯示位置會錯誤

先來說說在安卓7.0以上setFocusable(fasle)位置顯示不正確的問題。

第一種解決方案:將setFocusable(true) (相當於說了廢話)

第二種解決方案:將showAsDropDown重寫(呼叫自己重寫的showAsDropDown, 將PopupWindow 當引數傳進去

public void showAsDropDown(PopupWindow pw, View anchor, int xoff, int yoff) {
        if (Build.VERSION.SDK_INT >= 24) {
            Rect rect = new Rect();
            anchor.getGlobalVisibleRect(rect);
            int h = anchor.getResources().getDisplayMetrics().heightPixels - rect.bottom;
            pw.setHeight(h);
            pw.showAsDropDown(anchor, xoff, yoff);
        } else {
            pw.showAsDropDown(anchor, xoff, yoff);
        }
    }
還有一個問題 我既想要點選外外部能讓外部控制元件有反應 又要讓內部的editView彈框呢(其實就是上面遺留的問題)


先說說在setFocusable(fasle)的情況下  在這種情況下高度寫成WRAP_CONTENT外部自然是不需要處理的 那麼就剩下如何讓鍵盤彈出了

對pop內部的editView實現監聽事件 使他強制彈框

popEdit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
		//強制彈出軟鍵盤
                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
            }
        });

然而這樣還不夠  鍵盤是彈出來了 可是被pop擋住了 需要在加上

mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
mPopupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
但是還是有個問題  就是第一次點選editView並不會彈出鍵盤  第二次才會彈出(如果你activity的xml佈局層級很深 可能需要點選很多次才會彈出  即使拿最外層的id去讓它彈出 依然需要2次) 至於這個問題 我並沒有什麼好的方法可以解決  如果知道怎麼解決的大佬請務必告訴我


由於setFocusable(fasle)的請求下並不能很好的解決 那麼只剩下setFocusable(true)的情況了
setFocusable(true)時 高度寫成WRAP_CONTENT 那麼內部的editView本身就是可以點選彈軟體盤的  那麼只剩下解決 點選外部控制元件不消失pop並且能響應對應控制元件的問題了

利用pop的觸控監聽來攔截

mPopupWindow.setTouchInterceptor(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                int height = v.getHeight();
                float touchY = event.getY();
                if (touchY > 0 && touchY < height) {
                    return false;	//點選pop內部時 不進行攔截
                }else {
                    MorePopOutSideTouchable(v, event);	//自己寫方法進行處理
                    return true;	//點選pop外部是 進行攔截 不讓事件傳遞下去
                }
            }
        });
這裡沒什麼特別好的方法 雖然解決了外部點選pop會消失 已經其他控制元件不響應的問題  但是在外部的區域 所有的控制元件  想要響應對應的點選事件
只能利用點選的X和Y的座標來和控制元件的X Y座標進行對比來實現對應控制元件的點選事件

private void MorePopOutSideTouchable(View v, MotionEvent event) {
	float x = event.getX();
        float y = event.getY();
	
}
最後我發現有些安卓的全面屏手機(系統在7.0以上)佈局的需求是下方有陰影(參考某寶的 “我的”-->“賬單”-->“篩選”或者“分類”的彈框)如果用setFocusable(fasle) 然後重寫showAsDropDown使其彈框位置正確  也會出現最下方不會被陰影覆蓋的情況(某寶暫時也沒解決) 那麼這個時候用setFocusable(true) 就可以解決了至於外部點選無效什麼的 上面也說了解決方法。

到此關於PopupWindow的說明就到此為止了,寫文不易,如需轉發請標註出處,謝謝大家了!!