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