什么是触摸事件分离?
屏幕上存在多个窗口时,多指触摸的情况下,多个手指的触摸事件可以分给不同的窗口,以下面的图为例,第一个手指按下,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查找窗口的方法内。如果两个窗口中有一个不支持分离事件,那么只有先按下的那个窗口可以响应触摸事件,另一个窗口并不会响应后面手指的触摸事件。比如在分屏的场景下,如果我们想要在操作我们应用的时候,另一个应用不响应事件,就可以将我们的应用配置成不支持分离事件