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的责任链的下一个节点继续处理。


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

相关推荐
CYRUS_STUDIO7 小时前
Frida 检测与对抗实战:进程、maps、线程、符号全特征清除
android·逆向
csj508 小时前
安卓基础之《(28)—Service组件》
android
lhbian10 小时前
PHP、C++和C语言对比:哪个更适合你?
android·数据库·spring boot·mysql·kafka
catoop11 小时前
Android 最佳实践、分层架构与全流程解析(2025)
android
ZHANG13HAO11 小时前
Android 13 特权应用(Android Studio 开发)调用 AOSP 隐藏 API 完整教程
android·ide·android studio
田梓燊11 小时前
leetcode 142
android·java·leetcode
angerdream12 小时前
Android手把手编写儿童手机远程监控App之JAVA基础
android
菠萝地亚狂想曲12 小时前
Zephyr_01, environment
android·java·javascript
sTone8737512 小时前
跨端框架通信机制全解析:从 URL Schema 到 JSI 到 Platform Channel
android·前端
sTone8737512 小时前
Java 注解完全指南:从 "这是什么" 到 "自己写一个"
android·前端