Android源碼_View事件分發

0,參考

Touch事件傳遞流程詳細剖析

收不到ACTION_MOVE事件

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章