圖解Android的事件分發(fā)

要從源碼理解整個(gè)事件傳遞過程,必須先從事件傳遞的返回值認(rèn)清整個(gè)事件的消費(fèi)過程,對(duì)整個(gè)事件有大致的了解,為何事件被消費(fèi),為何由子類消費(fèi)

需要注意的幾個(gè)點(diǎn)

主要這三個(gè)方法

dispatchTouchEvent() —— true消費(fèi),false不消費(fèi)(即為分發(fā));false表示事件停止往下面的視圖層級(jí)進(jìn)行傳遞,同時(shí)開始往上面的視圖層級(jí)的onTouchEvent傳遞,也稱為回溯。
oninterceptTouchEvent() —— true消費(fèi),false不消費(fèi)(即為不攔截)
onTouchEvent() —— true消費(fèi),false不消費(fèi)()

而事件分發(fā)一般會(huì)經(jīng)過視圖的三個(gè)層級(jí):Activity、ViewGroup、View。下表會(huì)對(duì)視圖不同的三個(gè)層級(jí)擁有的事件分發(fā)的相關(guān)方法進(jìn)行整理:

事件分發(fā)相關(guān)方法 方法功能 Activity ViewGroup View
public boolean dispatchTouchEvent 事件分發(fā) + + +
public boolean onInterceptTouchEvent 事件攔截 +
public boolean onTouchEvent 事件消費(fèi) + + +
  1. view因?yàn)闆]有孩子,沒有onInterceptTouchEvent

  2. onTouchEvent中對(duì)點(diǎn)擊事件的具體處理流程大概如下,只要View的CLICKABLE和LONG_CLICKABLE有一個(gè)為true,那么它就會(huì)消耗事件,返回true??偟膩碚f,View的可不可用不影響是否消耗事件,只要clickable或者longClickable有一個(gè)為true,那么它就會(huì)消耗事件。

  3. ViewGroup默認(rèn)不攔截任何事件,因?yàn)閺脑创a中可以看到ViewGroup的onInterceptTouchEvent方法默認(rèn)返回false.

  4. 由View中dispatchTouchEvent可知,如果一個(gè)控件是可點(diǎn)擊的,那么點(diǎn)擊該控件時(shí),dispatchTouchEvent的返回值必定是true

public boolean dispatchTouchEvent(MotionEvent event) {  
    if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&  
            mOnTouchListener.onTouch(this, event)) {  
        return true;  
    }  
    return onTouchEvent(event);  
}
  • ViewGroup中dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent ev) {  
    final int action = ev.getAction();  
    final float xf = ev.getX();  
    final float yf = ev.getY();  
    final float scrolledXFloat = xf + mScrollX;  
    final float scrolledYFloat = yf + mScrollY;  
    final Rect frame = mTempRect;  
    boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;  
    if (action == MotionEvent.ACTION_DOWN) {  
        if (mMotionTarget != null) {  
            mMotionTarget = null;  
        }  
        if (disallowIntercept || !onInterceptTouchEvent(ev)) {  
            ev.setAction(MotionEvent.ACTION_DOWN);  
            final int scrolledXInt = (int) scrolledXFloat;  
            final int scrolledYInt = (int) scrolledYFloat;  
            final View[] children = mChildren;  
            final int count = mChildrenCount;  
            for (int i = count - 1; i >= 0; i--) {  
                final View child = children[i];  
                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE  
                        || child.getAnimation() != null) {  
                    child.getHitRect(frame);  
                    if (frame.contains(scrolledXInt, scrolledYInt)) {  
                        final float xc = scrolledXFloat - child.mLeft;  
                        final float yc = scrolledYFloat - child.mTop;  
                        ev.setLocation(xc, yc);  
                        child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;  
                        if (child.dispatchTouchEvent(ev))  {  
                            mMotionTarget = child;  
                            return true;  
                        }  
                    }  
                }  
            }  
        }  
    }  
    boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||  
            (action == MotionEvent.ACTION_CANCEL);  
    if (isUpOrCancel) {  
        mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;  
    }  
    final View target = mMotionTarget;  
    if (target == null) {  
        ev.setLocation(xf, yf);  
        if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {  
            ev.setAction(MotionEvent.ACTION_CANCEL);  
            mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;  
        }  
        return super.dispatchTouchEvent(ev);  
    }  
    if (!disallowIntercept && onInterceptTouchEvent(ev)) {  
        final float xc = scrolledXFloat - (float) target.mLeft;  
        final float yc = scrolledYFloat - (float) target.mTop;  
        mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;  
        ev.setAction(MotionEvent.ACTION_CANCEL);  
        ev.setLocation(xc, yc);  
        if (!target.dispatchTouchEvent(ev)) {  
        }  
        mMotionTarget = null;  
        return true;  
    }  
    if (isUpOrCancel) {  
        mMotionTarget = null;  
    }  
    final float xc = scrolledXFloat - (float) target.mLeft;  
    final float yc = scrolledYFloat - (float) target.mTop;  
    ev.setLocation(xc, yc);  
    if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {  
        ev.setAction(MotionEvent.ACTION_CANCEL);  
        target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;  
        mMotionTarget = null;  
    }  
    return target.dispatchTouchEvent(ev);  
}
  1. 子view的onTouchEvent如果默認(rèn)false不消費(fèi)的話,會(huì)交給上級(jí)的onTouchEvent

  2. viewGroup如果onInterceptTouchEvent不攔截,繼續(xù)交給子view

  3. viewGroup如果dispatchTouchEvent返回true,消費(fèi)掉會(huì)還給上級(jí)的onTouchEvent

如果上面的點(diǎn)你都清楚的話,說明對(duì)事件分發(fā)是有清楚認(rèn)識(shí)的,那么接下來上圖

android事件分發(fā)圖解.png
事件鏈
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容