Android笔记-从ViewGroup的dispatchTouchEvent源码分析事件分发机制

前一篇文章:浅析了事件拦截机制
主要是从demo中看的现象总结的结论
文中涉及到以下方法
1. ViewGroup的三个方法:
dispatchTouchEvent:事件分发
onInterceptTouchEvent:事件拦截
onTouchEvent:事件触发

  1. View的两个方法
    dispatchTouchEvent:事件分发
    onTouchEvent:事件触发

而且当时我们忽略了一个方法dispatchTouchEvent
其实dispatchTouchEvent这个方法其实才是处理事件分发拦截的主要方法
今天我们重点解析此方法。

Activity中的dispatchTouchEvent

当我们点击屏幕时候,事件到底怎么传递的呢
其实在Activity中也有dispatchTouchEvent方法,我们来看源码:

Activity:

public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

onUserInteraction();其实是一个空方法,我们忽略。
如果getWindow().superDispatchTouchEvent(ev)方法返回的是true,则返回true,否则执行Activity的onTouchEvent(ev);

我们再看如果getWindow().superDispatchTouchEvent(ev)方法,其实是用的抽象类Window中的superDispatchTouchEvent方法,最终实现类是PhoneWindow,我们再看PhoneWindow的superDispatchTouchEvent方法:

PhoneWindow:

 @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
    }

mDecor是一个DecorView,它继承了FrameLayout,其实就是Activity的根view。

 DecorView:

 public boolean superDispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }

我们看到其实最终触发的是ViewGroup的dispatchTouchEvent。
小结:activity中事件传递方向,如下图:

graph TD

A(Activity)-->|dispatchTouchEvent|B(PhoneWindow)
B-->|superDispatchTouchEvent|C(DecorView)
C-->|superDispatchTouchEvent|D{
  ViewGroup}
D-->|dispatchTouchEvent|E{
  ViewGroup}
D-->|dispatchTouchEvent|F(View)
E-->|dispatchTouchEvent|G(ViewGroup)
E-->|dispatchTouchEvent|K(View)

我们看到,最终是由ViewGroup来处理事件的。下面我们重点分析ViewGroup的dispatchTouchEvent方法。

ViewGroup和View中的dispatchTouchEvent
先看一下伪代码:
ViewGroup:
public boolean dispatchTouchEvent(MotionEvent event) {
        if(onInterceptTouchEvent(event)){
  //是否拦截
            return onTouchEvent(event);
        }
        //没有拦截
        if(child==null){
            //没有子控件
            return onTouchEvent(event);
        }else{
             //执行子控件的dispatchTouchEvent
            boolean consume= child.dispatchTouchEvent(event);
            if(!consume){
  //子控件没有消费事件,执行当前view的onTouchEvent
                return onTouchEvent(event);
            }else{
                return false;
            }
        }


    }


ViewGroup中:
看到:
如果onInterceptTouchEvent返回true,则执行当前ViewGroup的onTouchEvent。

如果onInterceptTouchEvent返回false: 如果有子控件,则向下传递事件,执行子控件的dispatchTouchEvent,如果没有子控件,则执行当前ViewGroup的onTouchEvent。

如果子控件的onInterceptTouchEvent返回false,则说明没有消费,当前控件执行onTouchEvent,否则返回false,表示所有子控件都没有消费事件,向上传递。

如果最上层控件也没有消费事件,则最终交给activity执行onTouchEvent事件。

View中:
先看下从ViewGroup中事件向下传递的逻辑:

 //向下传递事件,执行子控件的dispatchTouchEvent
            boolean consume= child.dispatchTouchEvent(event);
            if(!consume){
  //子控件没有消费事件,执行当前view的onTouchEvent
                return onTouchEvent(event);
            }else{
                return false;
            }

再看View中的dispatchTouchEvent,伪代码如下:

    View:
    public boolean dispatchTouchEvent(MotionEvent event) {
        return onTouchEvent(event);
    }

从上面代码看到,如果child如果是View,则会返回其onTouchEvent(event)方法的结果。当然View中还有
还有onTouchListener onClickListener等事件的触发。这些方法我们后面再细细分析。现在我们暂时只关注onTouchEvent方法。
该方法如果返回true,则表示view消费了事件,其父控件的onTouchEvent方法将不会触发。
如果返回false,父控件则触发onTouchEvent方法并返回onTouchEvent方法的结果。

dispatchTouchEvent源码分析
  public boolean dispatchTouchEvent(MotionEvent ev) {
        //验证事件是否连续
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
        }
        //这个变量用于记录事件是否被处理完
        boolean handled = false;
        //过滤掉一些不合法的事件:当前的View的窗口被遮挡了。
        if (onFilterTouchEventForSecurity(ev)) {
            //如果事件发生的View在的窗口,没有被遮挡
            final int action = ev.getAction();
            //重置前面为0 ,只留下后八位,用于判断相等时候,可以提高性能。
            final int actionMasked = action & MotionEvent.ACTION_MASK;
            //判断是不是Down事件,如果是的话,就要做初始化操作
            if (actionMasked == MotionEvent.ACTION_DOWN) {
                //如果是down事件,就要清空掉之前的状态,比如,重置手势判断什么的。
                //比如,之前正在判断是不是一个单点的滑动,但是第二个down来了,就表示,不可能是单点的滑动,要重新开始判断触摸的手势
                //清空掉mFirstTouchTarget
                // Throw away all previous state when starting a new touch gesture.
                // The framework may have dropped the up or cancel event for the previous gesture
                // due to an app switch, ANR, or some other state change.
                cancelAndClearTouchTargets(ev);
                resetTouchState();
            }


            //检查是否拦截事件
            final boolean intercepted;
            //如果当前是Down事件,或者已经有处理Touch事件的目标了
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                //判断允不允许这个View拦截
                //使用与运算作为判断,可以让我们在flag中,存储好几个标志
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                //如果说允许拦截事件
                if (!disallowIntercept) {
                    //确定是不是拦截了
                    intercepted = onInterceptTouchEvent(ev);
                    //重新恢复Action,以免action在上面的步骤被人为地改变了
                    ev.setAction(action); 
  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值