0,参考
Android源码
1-1,测试Demo的xml样例
(A、B继承Framelayout;C继承View,未修改任何参数)
<LinearLayout>
<A100>
<B110>
<C111>
</C111>
<C112>
</C112>
</B110>
<B120>
</B120>
</A100>
<A200>
<B210>
<C211>
</C211>
</B210>
<B220>
</B220>
</A200>
<A300>
<B310>
</B310>
</A300>
</LinearLayout>
1-2,全部都是默认值时的时序图(默认值 = false)
1-3,修改函数返回值时的时序图
dispatchTouchEvent | onInterceptTouchEvent | onTouchEvent | |
A | A.dispatch = true | A.onIntercept = true | A.onTouch = true |
B | B.dispatch = true | B.onIntercept = true | B.onTouch = true |
C | C.dispatch = true | 无实现
| C.onTouch = true |
2-0,提问
1)C111的点击事件,DecorView会dispatch给A300么
2)用户从A300滑动到A100,会触发A100的事件分发么
3)dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent的简化逻辑
4)为什么修改dispatchTouchEvent或onTouchEvent的返回值时,会调用多次
2-1,C111的点击事件,DecorView会dispatch给A300嘛
不会,因为被不符合条件;(父类判断、坐标点判断),代码段如下:
#ViewGroup.java
public boolean dispatchTouchEvent(MotionEvent ev) {
...
View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
? findChildWithAccessibilityFocus() : null;
// 通过,目标View,instanceOf去判断是否是某一个父类;若不是,则跳过
if (childWithAccessibilityFocus != null) {
if (childWithAccessibilityFocus != child) {
continue;
}
...
}
// 通过坐标判断是否在范围内;若不在范围内,则跳过
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
...
}
#ViewGroup.java
private View findChildWithAccessibilityFocus() {
ViewRootImpl viewRoot = getViewRootImpl();
if (viewRoot == null) {
return null;
}
View current = viewRoot.getAccessibilityFocusedHost();
if (current == null) {
return null;
}
ViewParent parent = current.getParent();
while (parent instanceof View) {
if (parent == this) {
return current;
}
current = (View) parent;
parent = current.getParent();
}
return null;
}
#ViewGroup.java
protected boolean isTransformedTouchPointInView(float x, float y, View child,
PointF outLocalPoint) {
final float[] point = getTempPoint();
point[0] = x;
point[1] = y;
transformPointToViewLocal(point, child);
final boolean isInView = child.pointInView(point[0], point[1]);
if (isInView && outLocalPoint != null) {
outLocalPoint.set(point[0], point[1]);
}
return isInView;
}
2-2,从A300滑动到A100,会触发A100的事件分发么
不会,因为ACTION_DOWN时特殊处理,当前事件被A300消化;简化代码如下:
#ViewGroup.java
public boolean dispatchTouchEvent(MotionEvent ev) {
...
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) { // 确定屏幕状态为正常状态(未被遮挡等)
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// 首次按下时,ACTION_DOWN特殊处理
if (actionMasked == MotionEvent.ACTION_DOWN) {
cancelAndClearTouchTargets(ev);
resetTouchState();
}
... 内有递归
// 结束,ACTION_UP特殊处理
if (canceled
|| actionMasked == MotionEvent.ACTION_UP
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
resetTouchState();
}
}
...
return handled;
}
2-3,dispatch、onIntercept、onTouch的简化逻辑
#ViewGroup.java
public void dispatchTouchEventInner(MotionEvent ev) {
boolean intercepted = onInterceptTouchEvent(ev);
// 如果不向子控件传递,子控件也不可能消费事件
if (!intercepted){
// 其他事件时,子控件可能进入递归,也可能调用onTouchEvent结束事件分发
// 这个canceled会动态修改的
dispatchTransformedTouchEvent(ev, canceled, child, bits);
}
}
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits) {
if (满足某些条件,退出递归) {
return false;
}
boolean handled;
if (null == child) {
handled = onTouchEvent(event); // 调用onTouch方法
} else {
handled = child.dispatchTouchEvent(event); // 当child == this时,进入递归
}
return handled;
}
2-4,为什么修改dispatch或onTouch的返回值时,会调用多次
当dispatchTouchEvent的返回值为true,(onTouchEvent 为true最终也是影响它),会进入2-3的递归逻辑
3,总结
1,默认值都是false,ACTION_MOVE不出现
2,dispatchTouchEvent、onTouchEvent返回true,父控件的onTouchEvent不会再执行,并且,一次点击分为ACTION_DOWN + ACTION_UP等多个操作
3,onInterceptTouchEvent返回true,子控件的dispatchTouchEvent、onTouchEvent不会被执行了
ylineSign
QQ群:644213963