Zhihu ネチズンの質問: Android のフローティング ウィンドウでは、どのようにしてウィンドウがイベントに応答できるようになり、同時にその背後の場所がイベントを受信できるようになりますか?

質問: Android のフローティング ウィンドウでは、どのようにしてウィンドウがイベントに応答できるようになり、同時にその背後にある場所がイベントを受信できるようになりますか?

フローティングウィンドウをクリックすると、自分自身がイベントを受け取ることができ、後ろのエリアもイベントを受け取ることができ、それを達成する方法、戻ります。 false は無効です

ネイティブ Android メカニズムは次の理由をサポートしていません。


この問題はシステムを改造しないと解決できませんが、まず、フローティングウィンドウもウィンドウであることから、
ウィンドウの背後にある領域は一般にアクティビティかウィンドウのみであり、本質もウィンドウです。

inputdispatcher の配布プロセスは、次のようなレイヤー状況のように、現在配布用に描画されているすべてのレイヤーもスキャンします。
dumpsys 入力を通じて、次のものを取得できます。


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 はディスパッチするために適切なウィンドウを上から下に検索します。上のウィンドウがディスパッチされると、下のウィンドウにはイベントをディスパッチしません。したがって、上のウィンドウは処理済みなので、それを下のウィンドウに渡したいとします。処理用のウィンドウ。

可能な解決策:

イベントを 2 つのウィンドウに直接送信できないことは上で説明しましたが、Android システムは他のタイプのソリューションも提供しています。たとえば、古典的なシーンでは、ダイナミック壁紙はタッチ イベントを受け入れることができ、壁紙上のランチャーも受け入れることができます。このシーン
はクラシックで、壁紙は別のウィンドウ レイヤとその下のレイヤに属し、ランチャーはアクティビティで別のウィンドウ レイヤに属し、L は壁紙の上のレイヤにありますが、デスクトップをタッチすると、次のことがわかります。下の壁紙も関連するタッチイベントを受信できます。
デスクトップここに画像の説明を挿入
と壁紙は 2 つのウィンドウ層に属しますが、明らかに両方のウィンドウが対応するタッチ イベントに応答できます。

つまり、この場所では、1 つの出来事が 2 つに引き継がれることがわかっていますが、ここでの原理は何でしょうか?

実際、ここの壁紙は、WallpaperService に同様の InputMonitor を実装しています。つまり、システムのすべてのイベントを受信する責任を負い、タッチ イベントがどのウィンドウに送出されても、それを受信でき、つまりすべてを監視します。システムのタッチ イベントをグローバルに管理します。

実際、この種のインプット コースは、Tencent 教室の Qianlima 先生のインプット コースでも説明されており、以前に使用した dumpsys のインプットからもわかります。

Global monitors on display 0:
    0: 'PointerEventDispatcher0 (server)', 

壁紙に加えて、画面上に表示されるタッチ トラック、systemui ジェスチャー、デスクトップ マルチタスク ジェスチャーなど、このモニターには多くの実装があります。 /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 でイベントを受け入れることができます。

ここに画像の説明を挿入

これには、アプリに関連する権限が必要です。

  if (!checkCallingPermission(android.Manifest.permission.MONITOR_INPUT,
                "monitorGestureInput()")) {
    
    
            throw new SecurityException("Requires MONITOR_INPUT permission");
        }

おすすめ

転載: blog.csdn.net/learnframework/article/details/131348365