Android 从源码的角度分析事件分发流程

一、这里讲的是单点触摸,一个事件序列是由 down – move – up 组成
1、首先当手指点击屏幕的时候,Activity会拿到这个事件,经过一层一层的分发,最终才能到达View上。

// 这个就是Activity的dispatchTouchEvent方法
public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
   		// 然后调用window 的 dispatchTouchEvent方法
   		// 也就是调用PhonWindow的superDispatchTouchEvent方法
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

2、调用DecorView 的 superDispatchTrackballEvent,最终就调用到ViewGroup的dispatchTouchEvent方法。

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

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

二、先讲事件的处理流程
1、事件的处理是从View的dispatchTouchEvent方法看起
在这里插入图片描述

textView.setOnTouchListener(new View.OnTouchListener() {
   @Override
   public boolean onTouch(View v, MotionEvent event) {
       return true;
   }
});

当设置了TouchListener之后,这个 mListenerInfo 就会为null,假设onTouch方法返回true,那么这个View的OnClick事件就不会执行,因为 li.mOnTouchListener.onTouch(this, event) 返回为true,if成立,result = true 下面 onTouchEvent(event)就不会执行

2、如果onTouch返回为false,那么就会执行 onTouchEvent(event)方法,在MotionEvent.ACTION_UP的时候调用 performClickInternal()方法,最终调用 performClick()方法。
在这里插入图片描述
三、先讲事件的分法流程
1、假设有一下案例
// 第一层层级 — 总经理 —ViewGroup
// 第二层层级 — 总监 —ViewGroup
// 第一层层级 — 清洁 —View
当down 事件到达总经理身上的时候,假设自己拦截处理,也就是onInterceptTouchEvent方法返回为true,那么就调用这个方法 dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS) 注意第三个参数为null
在这里插入图片描述
最终就调到View的dispatchTouchEvent来处理这个事件
在这里插入图片描述
2、当onInterceptTouchEvent 不拦截事件,那么就返回false,if (!canceled && !intercepted) {}就进入这个if语句中
在这里插入图片描述
在这里插入图片描述
调用dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)这个方法,传递的child也就是总监,然后调用总监的dispatchTouchEvent方法,然后在走一圈流程(又到了ViewGroup的dispatchTouchEvent方法中去),由于总监也没有处理,最终交给清洁工处理,清洁工实质是View,那么就调到View的dispatchTouchEvent方法中,假设View(清洁工)设置了 OnTouchListener监听,并且onTouch方法返回为true,那么看下下图
在这里插入图片描述
那么下面的if条件成立,mFirstTouchTarget 就等于newTouchTarget
但是 newTouchTarget.next 为null,然后跳出循环。
在这里插入图片描述
在这里插入图片描述
down事走完之后,就已经决定了由谁来处理这个事件
2、当move事件达到的时候,也还是先走总经理、和总监,但是不会分发事件了。先调用总经理的 dispatchTransformedTouchEvent方法,然后调用child.dispatchTouchEvent(event),这个child此时为总监,最后才调用到 清洁工的dispatchTouchEvent方法,也就是View的dispatchTouchEvent方法。
在这里插入图片描述
3、拦截流程,只能在move事件中处理
ViewPager中显示ListView,此时会有事件冲突。
内部拦截法,子View来处理

ListView代码
@Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                int deltaX = x - mLastX;
                int deltaY = y - mLastY;
                if (Math.abs(deltaX) > Math.abs(deltaY)) {
                    getParent().requestDisallowInterceptTouchEvent(false);
                }
                break;
            }
            case MotionEvent.ACTION_UP: {
                break;

            }
            default:
                break;
        }
        mLastX = x;
        mLastY = y;
        return super.dispatchTouchEvent(event);
    }
    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN){
            super.onInterceptTouchEvent(event);
            return false;
       }
    return true;
  }

外部拦截法,父View来处理

@Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                mLastX = (int) event.getX();
                mLastY = (int) event.getY();
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                int deltaX = x - mLastX;
                int deltaY = y - mLastY;
                if (Math.abs(deltaX) > Math.abs(deltaY)) {
                    return true;
                }
                break;
            }
            case MotionEvent.ACTION_UP: {
                break;
            }
            default:
                break;
        }
        return super.onInterceptTouchEvent(event);
    }

当事件被ViewPager进行拦截的时候,ListView会调用Cancel事件,Cancel事件是: 当事件被上层拦截的时候才会触发

最重要的结论:

1、只要是两个View有叠加的部分,当手指触摸到这块的话,那么就有事件冲突

2、事件冲突只能在 move事件中进行

3、内部拦截法: 子view处理事件冲突,外部拦截法是父容器处理

4、父容器可以抢子View的事件

5、子view不可以抢父容器的事件

6、子view一旦拿到了事件,事件再由谁处理就是由子view来处理

7、如果为down事件才分发,如果down事件没有拿到的话,那么move和up事件就和我没有关系了(针对叶节点的,就是最后一个View)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值