每日一問 為什么 Dialog 彈出后 Activity 就無法響應(yīng)用戶事件了?

這個(gè)問題很簡(jiǎn)單,這個(gè)是因?yàn)镈ialog是Focusable的。如果不是Focusable的,那么Dialog 彈出后 Activity 還是可以響應(yīng)用戶事件的。

首先要明確一個(gè)概念是窗口是事件分發(fā)的基本單位,而不是activity或者應(yīng)用。系統(tǒng)的事件總是要先分給窗口,決定要分給哪個(gè)窗口是在系統(tǒng)的InputDispatcher.cpp這個(gè)類中。

對(duì)于touch事件,還要再由窗口交給DecorView在控件樹中進(jìn)行分發(fā),最終到達(dá)某個(gè)View。但在此之前,會(huì)交由activity進(jìn)行處理一下。

可以用以下demo測(cè)試一下:

view = new MyButton(this);
getWindowManager().addView(view, getWindowParams());

    private WindowManager.LayoutParams getWindowParams(){
        WindowManager.LayoutParams params = new WindowManager.LayoutParams();
        params.format = PixelFormat.TRANSLUCENT;// 支持透明
        /*這種接收時(shí)間的范圍大小是整個(gè)屏幕*/
//        params.flags |= WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;// 接受窗口外事件
        /*這個(gè)接收事件的范圍是自己設(shè)置的大小*/
        params.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        params.width = 490;//窗口的寬和高
        params.height = 160;
        params.x = 0;//窗口位置的偏移量
        params.y = 50;
        params.setTitle("MyDialog");
        params.gravity = Gravity.START|Gravity.TOP;
        return params;
    }

    public void dismiss(){
        getWindowManager().removeView(view);
    }

    class MyButton extends AppCompatButton {

        private TestWmsActivity activity;
        public MyButton(TestWmsActivity context) {
            super(context);
            activity = context;
        }

        @Override
        public boolean onTouchEvent(MotionEvent event) {
            Log.d(Config.TAG, "onTouchEvent: " + event.getAction());
            activity.dismiss();
            return true;

        }

        @Override
        public boolean onKeyDown(int keyCode, KeyEvent event) {
            Log.d(Config.TAG, "onKeyDown: " + keyCode);
            activity.dismiss();
            return true;
        }
    }

getWindowManager().addView 這里的添加view其實(shí)是添加一個(gè)子窗口。添加完MyDialog以后,我們的activity一共就有兩個(gè)窗口了,另外一個(gè)是默認(rèn)生成的子窗口。這一點(diǎn)可以通過adb shell dumpsys window visible來查看:

Window #6 Window{57cc6f1 u0 MyDialog}:
   ...
Window #7 Window{a38a957 u0 com.example.xcm.demo/com.example.xcm.demo.wms.TestWmsActivity}:
   ...

主窗口的名字一般就是activity的ComponentName。子窗口的名字如果不調(diào)用params.setTitle()進(jìn)行設(shè)置,那么默認(rèn)也是所屬activity的ComponentName,只是前面的地址不一樣而已。

可以看到,子窗口是在主窗口上面,即子窗口的z序是小于主窗口的。

我們的MyDialog 沒有加上params.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 這句話的時(shí)候,子窗口的touch區(qū)域其實(shí)是整個(gè)Frame,也就是主窗口完完全全被子窗口擋住了,自然無法響應(yīng)用戶事件。這個(gè)說法有什么證據(jù)嗎?可以通過adb shell dumpsys input 來查看一下:

在Input Dispatcher State:一欄中,有以下信息:

  6: name='Window{e3f21ef u0 MyDialog}',  paused=false, hasFocus=true visible=true, canReceiveKeys=true frame=[590,1127][1080,1287], **touchableRegion=[0,0][1080,2340]**,
            7: name='Window{d68c785 u0 com.example.xcm.demo/com.example.xcm.demo.wms.TestWmsActivity}', paused=false, hasFocus=false,  visible=true, canReceiveKeys=true, frame=[0,0][1080,2340], **touchableRegion=[0,0][1080,2340]**

可以看到雖然子窗口的看起來只有 frame=[590,1127][1080,1287] 這么大,但其touchableRegion=[0,0][1080,2340]卻是全屏的,和主窗口一樣大,又在主窗口上面,于是擋住了主窗口;所以主窗口,即大家經(jīng)常說的acitivity,無法接收到touch事件。只有當(dāng)子窗口接受到事件dismiss以后,才能接收到事件。

當(dāng)MyDialog 加上params.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 這句話的時(shí)候 ,它的touch區(qū)域發(fā)生了變化。

6: name='Window{e3f21ef u0 MyDialog}',  paused=false, hasFocus=false visible=true, canReceiveKeys=false frame=[590,1127][1080,1287], **touchableRegion=[590,1127][1080,1287]**,

現(xiàn)在子窗口看起來多大,能接受touch事件的區(qū)域也就多大了,其余區(qū)域不會(huì)在阻攔主窗口的事件。而且現(xiàn)在子窗口無法接受key事件,canReceiveKeys=false。

最后,為什么谷歌要這么做?原因不是很清楚,可以猜測(cè)一下:如果一個(gè)窗口是Focusable的,那么其是要響應(yīng)全局事件的,因?yàn)镕ocusable對(duì)應(yīng)著key事件,意味可以接受key事件,而key事件是全局的(沒有一個(gè)具體的區(qū)域,touch事件是有局域的,就是touchableRegion),所以為了統(tǒng)一,也把touchableRegion改成了全屏。如果你調(diào)反過來,如果一個(gè)子窗口是不能接受key事件的,那么它實(shí)際上多大,它的touchableRegion也就是多大。
總結(jié):

因?yàn)锳ndroid中特意淡化了Window的概念,所以可能很多同學(xué)只知道activity而不知道Window。如果弄清楚了Window在事件分發(fā)中的作用,這個(gè)問題還是容易理解的。

結(jié)論就是子窗口彈出了以后,因?yàn)樗诖翱诘纳厦?,而且他的touchableRegion是全屏,擋住了主窗口,所以系統(tǒng)把touch事件都發(fā)給子窗口了,主窗口無法響應(yīng)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容