Android View事件分发没那么复杂--极简化流程(核心源码)

从ViewRootImpl到Decorview, 从DecorView交给Activity, Activity通过windowphone从DecorView开始分发.

自上而下思维,本文梳理枝干,不关注太细节的东西。

java 复制代码
#DecorView.java

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    final Window.Callback cb = mWindow.getCallback();
    return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
            ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
//mWindow.getCallback()获取的就是Activity本身.
scss 复制代码
#Activity.java

public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    return onTouchEvent(ev);
}
csharp 复制代码
#PhoneWindow.java

@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
    return mDecor.superDispatchTouchEvent(event);
}
csharp 复制代码
#DecorView.java

public boolean superDispatchTouchEvent(MotionEvent event) {
    //调用父类ViewGroup的分发方法开始真正View体系中的事件分发
    return super.dispatchTouchEvent(event);
}

后面就是ViewGroup的具体分发流程.

VG重写了继承的父类View的dispatchTouchEvent()

return true则代表消费时间,停止继续分发。反之则反之。

关键核心代码。

java 复制代码
#ViewGroup.java

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {

    if (!canceled && !intercepted) {
        //如果不拦截,会遍历子View进行分发事件
        for (int i = childrenCount - 1; i >= 0; i--) {
            
            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
            //找到了需要消费的子View--child,下面代码会将mFirstTouchTarget赋值为该child
            newTouchTarget = addTouchTarget(child, idBitsToAssign);
            
        }
    }
    ...
    //如果拦截或者没有子View消费事件,此处mFirstTouchTarget会为空,
    //会将child参数设为null。其实就是看vg自己是否要消费事件
    if (mFirstTouchTarget == null) {
        // No touch targets so treat this as an ordinary view.
        handled = dispatchTransformedTouchEvent(ev, canceled, null,
                TouchTarget.ALL_POINTER_IDS);
    }
    //如果mFirstTouchTarget!=null,说明没有拦截并且子View消费了事件,
    //那么该vg的事件分发其实已经在上面整个流程的代码中走完,下面是一些杂项处理。
    ...
            
    return handled;
}


private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
        View child, int desiredPointerIdBits) {
        ...
        
        if (child == null) {
            //child为空,说明VG拦截了事件或者没有找到可以消费事件的子View,
            //那么就调用父类View的dispatchTouchEvent()来自己处理
            handled = super.dispatchTouchEvent(event);
        } else { 
            //此处将事件分发给子View,如果所有子View都不处理那么其实又会
            //调用上面child == null的代理,结合上面dispatchTouchEvent中流程去理解。
            //如果子View也是个vg,那么又会调用该vg的dispatchTouchEvent()。嵌套调用
            handled = child.dispatchTouchEvent(event);
        }
        ...
        
        return handled;
}

VG先判断是否拦截,如果拦截

则交个自己重写的的dispatchtouchevent()处理,依次调用onTouchListener.onTouch()和onTouchEvent(),onTouchEvent()内部又会调用onClickListerner.click()的回调如果前面的调用消费了事件则不会走到后面。 如果本身并没有消费事件,那么返回false

向上传递.

ini 复制代码
#View.java

public boolean dispatchTouchEvent(MotionEvent event) {
    ...
    ListenerInfo li = mListenerInfo;
    if (li != null && li.mOnTouchListener != null
            && (mViewFlags & ENABLED_MASK) == ENABLED
            && li.mOnTouchListener.onTouch(this, event)) {
        result = true;
    }
    
    if (!result && onTouchEvent(event)) {
        result = true;
    }
    ...
}

如果某个view消费了事件,那么vg会记录该view,后面的事件都会交给其处理.

VG如果不拦截事件

那么VG就会遍历所有子View,交给子View的dispatchtouchevent()处理。内部流程和上面VG调用父类View的dispatchTouchEvent()是一样的。 依次调用onTouchListener.onTouch()和onTouchEvent(),onTouchEvent()内部又会调用onClickListerner.click()的回调如果前面的消费则不会走到后面。

VG判断所有子view都未消费事件,则会判断自身是否需要消费,交给自己继承的父类View的dispatchtouchevent(),也就是调用super.dispatchtouchevent()方法,这和上面拦截时流程又一致了。如果VG本身也都没有消费,那么返回false。

继续向上传递. 重复此过程


如果都未消费,最后到达Activity,如果也未消费,直到又回到DecorView.最终到达ViewRootImpl的责任链的下一个节点继续处理。


如有出入的部分,还请指教

相关推荐
Reese_Cool1 小时前
【C语言二级考试】循环结构设计
android·java·c语言·开发语言
平凡シンプル2 小时前
安卓 uniapp跨端开发
android·uni-app
elina80132 小时前
安卓实现导入Excel文件
android·excel
严文文-Chris2 小时前
【设计模式-享元】
android·java·设计模式
趋势大仙2 小时前
SQLiteDatabase insert or replace数据不生效
android·数据库
DS小龙哥2 小时前
QT For Android开发-打开PPT文件
android·qt·powerpoint
试行3 小时前
Android实现自定义下拉列表绑定数据
android·java
Dingdangr8 小时前
Android中的Intent的作用
android
技术无疆8 小时前
快速开发与维护:探索 AndroidAnnotations
android·java·android studio·android-studio·androidx·代码注入
GEEKVIP8 小时前
Android 恢复挑战和解决方案:如何从 Android 设备恢复删除的文件
android·笔记·安全·macos·智能手机·电脑·笔记本电脑