PopupWindow之踩坑(1) setFocusable與setOutsideTouchable問題

這是我最近遇到的問題僚纷,由于之前對PopupWindow使用不熟悉况鸣,理解不透徹導(dǎo)致的怔锌,所以現(xiàn)在對這些方法進行了盡量的深入解析侈咕,另外總結(jié)就是不能只管去拷貝復(fù)制別人的代碼,然后發(fā)現(xiàn)不符合自己的要求了翅睛,就一個一個去改著試試,這樣盡管最后可能達到了要求,但卻不知其所以然址儒,后續(xù)還極有可能突然就蹦出來個bug。

  • 是否正確理解setFocusable(boolean focusable)setOutsideTouchable(boolean touchable)衅疙?

要徹底弄清這個問題莲趣,就要分別查看android5.0以下和5.0以上(含5.0)的源碼,因為android系統(tǒng)創(chuàng)建popup的時候在5.0以上和以上會有不同的處理饱溢,5.0以下的系統(tǒng)在創(chuàng)建popup的時候會根據(jù)你是否通過調(diào)用popup的setBackgroundDrawable(Drawable background)方法來判斷是否把你的popup放到一個PopupViewContainer里面妖爷,5.0以下的源碼如下(對無關(guān)代碼做了省略):

private void preparePopup(WindowManager.LayoutParams p) {
       ......
        if (mBackground != null) {
            PopupViewContainer popupViewContainer = new PopupViewContainer(mContext);
            PopupViewContainer.LayoutParams listParams = new PopupViewContainer.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, height
            );
            popupViewContainer.setBackgroundDrawable(mBackground);
            popupViewContainer.addView(mContentView, listParams);   //重點,只有mBackground != null時才會執(zhí)行

            mPopupView = popupViewContainer;
        } else {
            mPopupView = mContentView;
        }
    }

而5.0以上(含5.0)的代碼是這樣的:

 private void preparePopup(WindowManager.LayoutParams p) {
        .......
        if (mBackground != null) {
            mBackgroundView = createBackgroundView(mContentView);
            mBackgroundView.setBackground(mBackground);
        } else {
            mBackgroundView = mContentView;
        }

        mDecorView = createDecorView(mBackgroundView);  //重點理朋,和mBackground無關(guān)絮识,都會執(zhí)行

    }

5.0以上系統(tǒng)的PopupDecorView就是5.0以下的PopupViewContainer,它們是同一個東西嗽上,這個containerView繼承自FrameLlayout次舌,它對onTouchEvent方法進行了重寫:

 private class PopupDecorView extends FrameLayout {
        ......
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            final int x = (int) event.getX();
            final int y = (int) event.getY();

            if ((event.getAction() == MotionEvent.ACTION_DOWN)
                    && ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
                dismiss();
                return true;
            } else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
                dismiss();
                return true;
            } else {
                return super.onTouchEvent(event);
            }

}

看到這,恍然大悟兽愤,在5.0以下的系統(tǒng)彼念,當你沒有setBackgroundDrawable時,此時的popup是完全沒有處理你的屏幕點擊觸摸事件的能力的浅萧,包括你的物理返回按鍵也一樣不會響應(yīng)逐沙,這時候不管你去如何設(shè)置setOutsideTouchable都是沒有意義的,根本不會進入到onTouchEvent方法里面洼畅,也就走不到if (event.getAction() == MotionEvent.ACTION_OUTSIDE)這個邏輯判斷了吩案。

這個MotionEvent.ACTION_OUTSIDE很熟悉,就是通過這個action來判斷是否響應(yīng)外側(cè)點擊事件的帝簇,那么它是怎么觸發(fā)的呢徘郭?

當把 MotionEvent.ACTION_OUTSIDEsetOutsideTouchable(boolean touchable)放到一起,就會很明了了丧肴。

分析setOutsideTouchable方法:

先看看setOutsideTouchable(boolean touchable)的源碼:

public void setOutsideTouchable(boolean touchable) {  
   mOutsideTouchable = touchable;  
}  

然后再看看mOutsideTouchable哪里會用到

private int computeFlags(int curFlags) {  
    curFlags &= ~(WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);  
    …………  
    if (mOutsideTouchable) {  
        curFlags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;  
    }  
    …………  
    return curFlags;  
}  

這段代碼主要是用各種變量來設(shè)置window所使用的flag残揉;

既然說到了FLAG_WATCH_OUTSIDE_TOUCH,那我們來看看FLAG_WATCH_OUTSIDE_TOUCH所代表的意義:

WechatIMG1606.jpeg

這段話的意思是說芋浮,如果窗體設(shè)置了FLAG_WATCH_OUTSIDE_TOUCH這個flag抱环,那么 用戶點擊窗體以外的位置時,將會在窗體的MotionEvent中收到MotionEvetn.ACTION_OUTSIDE這個事件。

所以由于在PopupViewContainer中添加了對MotionEvent.ACTION_OUTSIDE事件的捕捉镇草,在當用戶點擊PopupViewContainer以外的區(qū)域時眶痰,PopupWindow這個窗體就會收到ACTION_OUTSIDE事件,然后調(diào)用dismiss方法來關(guān)閉掉自己陶夜。

讀到這凛驮,基本對setOutsideTouchable方法的來龍去脈了解的差不多了。

分析setFocusable方法:

再看setFocusable方法条辟,一般資料對此的解釋都是:是否讓Popupwindow獲得焦點黔夭,這確實揭示了本質(zhì),但卻很抽象羽嫡,不能從方法調(diào)用的級別來解釋該方法的作用本姥。其實,這個方法的觸發(fā)以及接收處理和setOutsideTouchable方法原理是一樣的:

public void setFocusable(boolean focusable) {
        mFocusable = focusable;
    }

接下來設(shè)置它的flag

if (!mFocusable) {
            curFlags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
            if (mInputMethodMode == INPUT_METHOD_NEEDED) {
                curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
            }
        }

這個flag比較牛逼杭棵,它能接收屏幕的 ACTION_UP婚惫,ACTION_DOWN等Touch事件,所以當設(shè)置了setFoucus為true時魂爪,因為PopupViewContainer對MotionEvent.ACTION_DOWN事件進行了捕捉先舷,然后同樣會調(diào)用自己的dissmiss方法來關(guān)閉自己。

現(xiàn)在也就很好明白為什么在設(shè)置了setFoucus(true)后再去設(shè)置 setOutsideTouchable(false)為什么沒有作用了滓侍,因為MotionEvent.ACTION_DOWN事件在MotionEvent.ACTION_OUTSIDE事件之前處理蒋川,消耗了MotionEvent事件。這樣也就能解釋官方對setOutsideTouchable方法的說明了

WechatIMG1613.jpeg

"控制是否通知popup窗體外的點擊事件撩笆,這個方法只有在touchable為true而focusable為false的時候才有意義", 說的很嚴謹捺球,是有沒有意義而不是有沒有作用,

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末夕冲,一起剝皮案震驚了整個濱河市氮兵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌歹鱼,老刑警劉巖泣栈,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異醉冤,居然都是意外死亡秩霍,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進店門蚁阳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鸽照,你說我怎么就攤上這事螺捐。” “怎么了?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵定血,是天一觀的道長赔癌。 經(jīng)常有香客問我,道長澜沟,這世上最難降的妖魔是什么灾票? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮茫虽,結(jié)果婚禮上刊苍,老公的妹妹穿的比我還像新娘。我一直安慰自己濒析,他們只是感情好正什,可當我...
    茶點故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著号杏,像睡著了一般婴氮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上盾致,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天主经,我揣著相機與錄音,去河邊找鬼庭惜。 笑死罩驻,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的蜈块。 我是一名探鬼主播鉴腻,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼百揭!你這毒婦竟也來了爽哎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤器一,失蹤者是張志新(化名)和其女友劉穎课锌,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體祈秕,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡渺贤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了请毛。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片志鞍。...
    茶點故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖方仿,靈堂內(nèi)的尸體忽然破棺而出固棚,到底是詐尸還是另有隱情统翩,我是刑警寧澤,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布此洲,位于F島的核電站厂汗,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏呜师。R本人自食惡果不足惜娶桦,卻給世界環(huán)境...
    茶點故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望汁汗。 院中可真熱鬧衷畦,春花似錦、人聲如沸碰酝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽送爸。三九已至铛嘱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間袭厂,已是汗流浹背墨吓。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留纹磺,地道東北人帖烘。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像橄杨,于是被迫代替她去往敵國和親秘症。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,585評論 2 359

推薦閱讀更多精彩內(nèi)容