问题:Android 悬浮窗怎么让窗口响应事件的同时,也能让背后挡住的地方收到事件?
点击悬浮窗,自己能收到事件,背后挡住的区域也要能收到,怎么实现,return. false 无效
原生android机制不支持原因:
这个问题如果不修改系统是无法完成的,首先根据腾讯课堂千里马的input专题知识可以知道:
悬浮窗也是个window
后面挡住区域一般也是个Activity或只window,本质也是window
inputdispatcher的派发流程也是会遍历当前绘制的所有layer进行派发,比如如下图层情况:
通过dumpsys input可以得到如下:
更多framework干货知识手把手教学
Log.i("千里马qq群",“422901085”);
Windows:
0: name='WindowManager', id=142, displayId=0, inputConfig=NOT_VISIBLE | NOT_FOCUSABLE, alpha=1.00, frame=[0,0][1440,2960], globalScale=1.000000, applicationInfo.name=WindowManager, applicationInfo.token=0x73b2aa4d1d90, touchableRegion=<empty>, ownerPid=562, ownerUid=1000, dispatchingTimeout=5000ms, hasToken=true, touchOcclusionMode=BLOCK_UNTRUSTED
transform (ROT_0) (IDENTITY)
1: name='StrictModeFlash', id=88, displayId=0, inputConfig=NO_INPUT_CHANNEL | NOT_VISIBLE | NOT_FOCUSABLE | NOT_TOUCHABLE | PREVENT_SPLITTING | TRUSTED_OVERLAY, alpha=1.00, frame=[0,0][1440,2960], globalScale=1.000000, applicationInfo.name=, applicationInfo.token=<null>, touchableRegion=<empty>, ownerPid=0, ownerUid=0, dispatchingTimeout=0ms, hasToken=false, touchOcclusionMode=BLOCK_UNTRUSTED
transform (ROT_0) (IDENTITY)
2: name='d4f3e23 NavigationBar0', id=78, displayId=0, inputConfig=NOT_FOCUSABLE | TRUSTED_OVERLAY | WATCH_OUTSIDE_TOUCH | SLIPPERY, alpha=1.00, frame=[0,2792][1440,2960], globalScale=1.000000, applicationInfo.name=, applicationInfo.token=<null>, touchableRegion=[166,2792][442,2960]|[580,2792][860,2960]|[996,2792][1273,2960], ownerPid=747, ownerUid=10099, dispatchingTimeout=5000ms, hasToken=true, touchOcclusionMode=BLOCK_UNTRUSTED
transform (ROT_0) (TRANSLATE)
1.0000 0.0000 -0.0000
0.0000 1.0000 -2792.0000
0.0000 0.0000 1.0000
3: name='affdd80 StatusBar', id=79, displayId=0, inputConfig=NOT_FOCUSABLE | TRUSTED_OVERLAY, alpha=1.00, frame=[0,0][1440,84], globalScale=1.000000, applicationInfo.name=, applicationInfo.token=<null>, touchableRegion=[0,0][1440,84], ownerPid=747, ownerUid=10099, dispatchingTimeout=5000ms, hasToken=true, touchOcclusionMode=BLOCK_UNTRUSTED
transform (ROT_0) (IDENTITY)
4: name='recents_animation_input_consumer', id=96, displayId=0, inputConfig=NOT_VISIBLE | TRUSTED_OVERLAY, alpha=1.00, frame=[0,0][1440,2960], globalScale=1.000000, applicationInfo.name=recents_animation_input_consumer, applicationInfo.token=0x73b2aa4c04b0, touchableRegion=[0,0][1440,2960], ownerPid=562, ownerUid=1000, dispatchingTimeout=5000ms, hasToken=true, touchOcclusionMode=BLOCK_UNTRUSTED
transform (ROT_0) (IDENTITY)
5: name='7c9d48b com.example.myapplication11/com.example.myapplication11.MainActivity', id=552, displayId=0, inputConfig=0x0, alpha=1.00, frame=[360,84][1080,972], globalScale=1.000000, applicationInfo.name=ActivityRecord{c71b56 u0 com.example.myapplication11/.MainActivity} t458}, applicationInfo.token=0x73b2aa4c5490, touchableRegion=[308,32][1133,1025], ownerPid=4415, ownerUid=10116, dispatchingTimeout=5000ms, hasToken=true, touchOcclusionMode=BLOCK_UNTRUSTED
transform (ROT_0) (SCALE TRANSLATE)
2.0000 -0.0000 -720.0000
-0.0000 2.0000 -168.0000
0.0000 0.0000 1.0000
6: name='SurfaceView[com.example.myapplication11/com.example.myapplication11.MainActivity](BLAST)#576', id=576, displayId=0, inputConfig=NO_INPUT_CHANNEL, alpha=1.00, frame=[360,256][360,256], globalScale=1.000000, applicationInfo.name=, applicationInfo.token=<null>, touchableRegion=<empty>, ownerPid=4415, ownerUid=10116, dispatchingTimeout=5000ms, hasToken=false, touchOcclusionMode=BLOCK_UNTRUSTED
transform (ROT_0) (SCALE TRANSLATE)
2.0000 -0.0000 -720.0000
-0.0000 2.0000 -512.0000
0.0000 0.0000 1.0000
7: name='7b00af5 ActivityRecordInputSink com.example.myapplication11/.MainActivity', id=550, displayId=0, inputConfig=NO_INPUT_CHANNEL | NOT_FOCUSABLE, alpha=1.00, frame=[360,84][360,84], globalScale=0.000000, applicationInfo.name=, applicationInfo.token=<null>, touchableRegion=[360,84][1080,972], ownerPid=562, ownerUid=1000, dispatchingTimeout=0ms, hasToken=false, touchOcclusionMode=BLOCK_UNTRUSTED
transform (ROT_0) (SCALE TRANSLATE)
2.0000 -0.0000 -720.0000
-0.0000 2.0000 -168.0000
0.0000 0.0000 1.0000
8: name='ed3ad5e ActivityRecordInputSink com.android.documentsui/.files.FilesActivity', id=481, displayId=0, inputConfig=NO_INPUT_CHANNEL | NOT_VISIBLE | NOT_FOCUSABLE | NOT_TOUCHABLE, alpha=1.00, frame=[0,0][0,0], globalScale=0.000000, applicationInfo.name=, applicationInfo.token=<null>, touchableRegion=[-14399,-29599][14400,29600], ownerPid=562, ownerUid=1000, dispatchingTimeout=0ms, hasToken=false, touchOcclusionMode=BLOCK_UNTRUSTED
transform (ROT_0) (IDENTITY)
9: name='a9d0b01 ActivityRecordInputSink com.android.traceur/.MainActivity', id=321, displayId=0, inputConfig=NO_INPUT_CHANNEL | NOT_VISIBLE | NOT_FOCUSABLE | NOT_TOUCHABLE, alpha=1.00, frame=[0,0][0,0], globalScale=0.000000, applicationInfo.name=, applicationInfo.token=<null>, touchableRegion=[-14399,-29599][14400,29600], ownerPid=562, ownerUid=1000, dispatchingTimeout=0ms, hasToken=false, touchOcclusionMode=BLOCK_UNTRUSTED
transform (ROT_0) (IDENTITY)
10: name='f3f7d36 ActivityRecordInputSink com.android.settings/.SubSettings', id=306, displayId=0, inputConfig=NO_INPUT_CHANNEL | NOT_VISIBLE | NOT_FOCUSABLE | NOT_TOUCHABLE, alpha=1.00, frame=[0,0][0,0], globalScale=0.000000, applicationInfo.name=, applicationInfo.token=<null>, touchableRegion=[0,0][1440,2960], ownerPid=562, ownerUid=1000, dispatchingTimeout=0ms, hasToken=false, touchOcclusionMode=BLOCK_UNTRUSTED
transform (ROT_0) (IDENTITY)
11: name='3053a19 ActivityRecordInputSink com.android.settings/.SubSettings', id=289, displayId=0, inputConfig=NO_INPUT_CHANNEL | NOT_VISIBLE | NOT_FOCUSABLE | NOT_TOUCHABLE, alpha=1.00, frame=[0,0][0,0], globalScale=0.000000, applicationInfo.name=, applicationInfo.token=<null>, touchableRegion=[0,0][1440,2960], ownerPid=562, ownerUid=1000, dispatchingTimeout=0ms, hasToken=false, touchOcclusionMode=BLOCK_UNTRUSTED
transform (ROT_0) (IDENTITY)
12: name='b298a7e ActivityRecordInputSink com.android.settings/.homepage.SettingsHomepageActivity', id=271, displayId=0, inputConfig=NO_INPUT_CHANNEL | NOT_VISIBLE | NOT_FOCUSABLE | NOT_TOUCHABLE, alpha=1.00, frame=[0,0][0,0], globalScale=0.000000, applicationInfo.name=, applicationInfo.token=<null>, touchableRegion=[0,0][1440,2960], ownerPid=562, ownerUid=1000, dispatchingTimeout=0ms, hasToken=false, touchOcclusionMode=BLOCK_UNTRUSTED
transform (ROT_0) (IDENTITY)
13: name='36c3662 com.android.launcher3/com.android.launcher3.uioverrides.QuickstepLauncher', id=565, displayId=0, inputConfig=DUPLICATE_TOUCH_TO_WALLPAPER, alpha=1.00, frame=[0,0][1440,2960], globalScale=1.000000, applicationInfo.name=ActivityRecord{f1bea28 u0 com.android.launcher3/.uioverrides.QuickstepLauncher} t452}, applicationInfo.token=0x73b2aa4b1390, touchableRegion=[0,0][1440,2960], ownerPid=1067, ownerUid=10095, dispatchingTimeout=5000ms, hasToken=true, touchOcclusionMode=BLOCK_UNTRUSTED
transform (ROT_0) (IDENTITY)
14: name='1ceec09 ActivityRecordInputSink com.android.launcher3/.uioverrides.QuickstepLauncher', id=102, displayId=0, inputConfig=NO_INPUT_CHANNEL | NOT_FOCUSABLE, alpha=1.00, frame=[0,0][0,0], globalScale=0.000000, applicationInfo.name=, applicationInfo.token=<null>, touchableRegion=[-14399,-29599][14400,29600], ownerPid=562, ownerUid=1000, dispatchingTimeout=0ms, hasToken=false, touchOcclusionMode=BLOCK_UNTRUSTED
transform (ROT_0) (IDENTITY)
15: name='Wallpaper BBQ wrapper#75', id=75, displayId=0, inputConfig=NO_INPUT_CHANNEL, alpha=1.00, frame=[-71,-147][2860,3108], globalScale=1.000000, applicationInfo.name=, applicationInfo.token=<null>, touchableRegion=<empty>, ownerPid=747, ownerUid=10099, dispatchingTimeout=5000ms, hasToken=false, touchOcclusionMode=BLOCK_UNTRUSTED
transform (ROT_0) (SCALE TRANSLATE)
0.3145 -0.0000 22.6451
-0.0000 0.3145 46.5455
0.0000 0.0000 1.0000
16: name='7ee1983 com.android.systemui.ImageWallpaper', id=74, displayId=0, inputConfig=NOT_FOCUSABLE | NOT_TOUCHABLE | PREVENT_SPLITTING | IS_WALLPAPER, alpha=1.00, frame=[-71,-147][-71,-147], globalScale=1.000000, applicationInfo.name=, applicationInfo.token=<null>, touchableRegion=<empty>, ownerPid=747, ownerUid=10099, dispatchingTimeout=5000ms, hasToken=true, touchOcclusionMode=BLOCK_UNTRUSTED
transform (ROT_0) (SCALE TRANSLATE)
0.3145 -0.0000 22.6451
-0.0000 0.3145 46.5455
0.0000 0.0000 1.0000
inputdispatch会从上到下进行寻找合适的window进行派发,一旦派发了上面的窗口,不会把事件派发到底下窗口,故你的这个上面window接受了处理了,还想传递给下面也处理是不行的
可以行方案:
上面已经讲解了不可以直接把事件传递到两个window,但是android系统也提供了其他的类是方案,比如经典场景就是:
动态壁纸可以接受触摸事件,壁纸上面的Launcher也可以接受触摸事件,这个场景就是经典的,Wallpaper属于单独的window图层再最底层,Launcher是个Activiyt也是单独的Window图层L位于Wallpaer的上层,但是触摸桌面时候发现下面的壁纸也是可以接受到相关的触摸事件。
桌面和wallpaper属于两个window图层,但是明显可以两个窗口都响应对应的触摸事件。
即这个地方就实现了一个事件可以传递到两个,那这里是什么原理呢?
哈其实这里壁纸是再WallpaperService中实现了一个类似InputMonitor,即它可以负责接受系统所有的事件,无论触摸事件派发到任何窗口上面,他都可以接受到,即全局监听了系统的所有触摸事件。
这种其实再腾讯课堂千里马老师的input课程里面也讲解过,用前面用过的dumpsys input也可以看出来:
Global monitors on display 0:
0: 'PointerEventDispatcher0 (server)',
除了壁纸外,同样也有很多这个monitor的实现方案,比如屏幕上显示的触摸轨迹,systemui的手势,桌面多任务手势等都是有这种实现.
具体实现:
packages/apps/Launcher3/quickstep/src/com/android/quickstep/TouchInteractionService.java
private void initInputMonitor(String reason) {
disposeEventHandlers("Initializing input monitor due to: " + reason);
if (mDeviceState.isButtonNavMode()) {
return;
}
mInputMonitorCompat = new InputMonitorCompat("swipe-up", mDeviceState.getDisplayId());
mInputEventReceiver = mInputMonitorCompat.getInputReceiver(Looper.getMainLooper(),
mMainChoreographer, this::onInputEvent);
mRotationTouchHelper.updateGestureTouchRegions();
}
接下来就可以在对应的onInputEvent接受事件:
这个是需要app有相关的权限:
if (!checkCallingPermission(android.Manifest.permission.MONITOR_INPUT,
"monitorGestureInput()")) {
throw new SecurityException("Requires MONITOR_INPUT permission");
}