Android 触摸事件分离原理

什么是触摸事件分离?
屏幕上存在多个窗口时,多指触摸的情况下,多个手指的触摸事件可以分给不同的窗口,以下面的图为例,第一个手指按下,window1可以响应这个事件,第二个手指按下(第一个手指不松开),window2同样也可以响应第二个手指按下的事件,两个手指的触摸事件被分离到了不同的窗口,这就是触摸事件分离。
在这里插入图片描述
可以通过配置FLAG_SPLIT_TOUCH(0x00800000)这个flag来设置window是否支持事件分离。默认情况下,系统中的窗口都是支持分离的,所以默认情况下,上图的场景,window1和window2都可以收到触摸事件。

当然我们可以禁用window的事件分离功能,如在主配置文件中配置:

android:windowEnableSplitTouch="false"

只要window1和window2中的任意一个不支持触摸分离,触摸事件只会分发给第一个手指按下的window,第二个手指按下的window将不会接收到触摸事件。

事件分离的原理

这部分逻辑在InputDispatcher 的 findTouchedWindowTargetsLocked方法中,该方法用于查找目标窗口

int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
                                                        const MotionEntry& entry,
                                                        std::vector<InputTarget>& inputTargets,
                                                        nsecs_t* nextWakeupTime,
                                                        bool* outConflictingPointerActions) {
	//省略
	TouchState tempTouchState;
    std::unordered_map<int32_t, TouchState>::iterator oldStateIt =
            mTouchStatesByDisplay.find(displayId);
    if (oldStateIt != mTouchStatesByDisplay.end()) {
        oldState = &(oldStateIt->second);
        tempTouchState.copyFrom(*oldState);//将上次的信息拷贝到tempTouchState中
    }

	bool isSplit = tempTouchState.split;

	bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN ||
                       maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction);//判断是否第一个手指按下

	if (newGesture) {//可以看出,第一个手指按下时,会清空tempTouchState,并将isSplit设为false
        bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
        //省略
        tempTouchState.reset();
        tempTouchState.down = down;
        tempTouchState.deviceId = entry.deviceId;
        tempTouchState.source = entry.source;
        tempTouchState.displayId = displayId;
        isSplit = false;
    }

	//省略
	if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {//1

		sp<InputWindowHandle> newTouchedWindowHandle =
                findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
                                          isDown /*addOutsideTargets*/, true /*addPortalWindows*/);//查找窗口
                                          
         // Figure out whether splitting will be allowed for this window.
        if (newTouchedWindowHandle != nullptr &&
            newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {//2,支持分离的话将isSplit 设为true
            // New window supports splitting, but we should never split mouse events.
            isSplit = !isFromMouse;
        } else if (isSplit) {// 3
            // New window does not support splitting but we have already split events.
            // Ignore the new window.
            newTouchedWindowHandle = nullptr;
        }

		// Handle the case where we did not find a window.
        if (newTouchedWindowHandle == nullptr) {
            // Try to assign the pointer to the first foreground window we find, if there is one.
            newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle();//4
        }
		
		if (newTouchedWindowHandle != nullptr) {
			//省略
			 tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);//添加进目标窗口
		}
	}else{
		//省略
		//第二根手指按下并且按下的是不支持事件分离的window时,走这个逻辑
	}
	//省略
}

还是以上图为例,用情景分析的方式,分析下上面的代码逻辑

1,window1和window2都支持分离事件
第一个手指按下的时候,newGesture 为ture,先清空tempTouchState,然后进入注释1的分支,查找到window1窗口后,因为window1窗口支持事件分离,则进入注释2处分支,将isSplit 设为true。然后就是window1加入到tempTouchState中,该事件可分发至window1窗口
第二个手指按下的时候,先将上次的信息拷贝到tempTouchState中(上次将isSplit 设置为true了),所以此次isSplit 也为true,因为不是第一根手指按下newGesture 为false,所以不会清空tempTouchState,但因为是第二根手指按下且isSplit 为true,所以也会进入注释1的分支。查找到window2窗口后,因为window2也支持分离事件,所以也是进入注释2处的分支将isSplit 设为true,然后就是window2加入到tempTouchState中,该事件可分发至window2窗口
所以window1和window2都支持分离事件的话,两个窗口都可以响应对应的触摸事件

2,先按下的window1支持分离事件,window2不支持
第一个手指按下的时候,newGesture 为ture,先清空tempTouchState,然后进入注释1的分支,查找到window1窗口后,因为window1窗口支持事件分离,则进入注释2处分支,将isSplit 设为true。然后就是window1加入到tempTouchState中,该事件可分发至window1窗口
第二个手指按下的时候,先将上次的信息拷贝到tempTouchState中(上次将isSplit 设置为true了),所以此次isSplit 也为true,因为不是第一根手指按下newGesture 为false,所以不会清空tempTouchState,但因为是第二根手指按下且isSplit 为true,所以也会进入注释1的分支。查找到window2窗口后,因为window2不支持分离事件且isSplit 为true,所以这次是进入注释3处的分支又将newTouchedWindowHandle 设为null,所以window2并不会加入到tempTouchState中,该事件就不会分发至window2窗口
所以这种情况下,仅有window1能响应触摸事件,而window2不会响应第二根手指的触摸事件

3,先按下的window1不支持分离事件,window2支持
第一个手指按下的时候,newGesture 为ture,先清空tempTouchState,然后进入注释1的分支,查找到window1窗口后,因为window1窗口不支持事件分离,所以不会设置isSplit 的值,isSplit 还是为false。然后把window1加入到tempTouchState中,该事件可分发至window1窗口
第二个手指按下的时候,先将上次的信息拷贝到tempTouchState中,此次isSplit 还是为false,所以注释1的分支根本就不会进入。注释1处都不会进入的话,就不会去查找窗口,即不会找到window2,该事件就不会分发至window2窗口
所以这种情况下,也是仅有window1能响应触摸事件,而window2不会响应第二根手指的触摸事件

总结
分离事件的逻辑在findTouchedWindowTargetsLocked查找窗口的方法内。如果两个窗口中有一个不支持分离事件,那么只有先按下的那个窗口可以响应触摸事件,另一个窗口并不会响应后面手指的触摸事件。比如在分屏的场景下,如果我们想要在操作我们应用的时候,另一个应用不响应事件,就可以将我们的应用配置成不支持分离事件

  • 7
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值