Android 事件分发细说

一直以来都对Android 的事件分发机制有疑惑,以前只知道事件分发涉及到三个方法:dispatchTouchEventonInterceptTouchEventonTouchEvent。三个方法分别为分发事件,拦截事件,处理事件,对于这些知识一知半解,并没有深入了解。当遇到滑动冲突的时候就在父View或者子View的这三个方法,return true or false,一个个试(笨~)。上网看别人的博客,篇幅太长并且看得我一愣一愣的,我有点方。今天终于理解的比较完整了,我尝试以一种直观简单的方法描述这里面的流程。

首先也是这3个方法:

  • dispatchTouchEvent,事件的分发,事件传到当前View的话一定会调用,内部调用了onInterceptTouchEventonTouchEvent,返回结果表示是否消耗当前事件,不消耗就向上层传递。
  • onInterceptTouchEvent,用来判断是否拦截某个事件。
  • onTouchEvent,用来判断是否消耗事件。

可能看了上面3个方法有点懵逼。这里一个一个的解释。

dispatchTouchEvent的内部实现伪代码简化之后是这样的。

        @Override
        public boolean dispatchTouchEvent(MotionEvent e){
            boolean consume = false;
            if(onInterceptTouchEvent(e)){
                comsume = onTouchEvent(e);
            }else{
                consume = child.dispatchTouchEvent(e);
            }
            return consume;
        }

上面的代码简单粗暴有木有?上面的代码把机制展现得淋漓尽致了。

首先调用onInterceptTouchEvent询问是否拦截,如果拦截,就由onTouchEvent处理,否则else就由childViewdispatchTouchEvent决定。

假设我们有以下场景。有一个Activity,然后里面嵌套了2个View,一个是ParentView,一个是ChildView。那么事件的传递是怎么样的呢。

事件的传递顺序是这样的:Activity -> Window -> View,假设我的ParentView得到事件之后,ParentView#dispatchTouchEvent来进行判断。然后调用了ParentView#onInterceptTouchEvent,如果ParentView#onInterceptTouchEvent返回true,就会调用ParentView#onTouchEvent。如果返回了false就会调用ChildView#dispatchTouchEvent,然后重复上面的过程。

这样很好理解,假设我在ParentView和ChildView的3个方法都没有做处理的话,事件的传递过程就是:

  1. ParentView -> dispatchTouchEvent
  2. ParentView -> onInterceptTouchEvent ->return false
  3. ChildView -> dispatchTouchEvent
  4. ChildView -> onInterceptTouchEvent
  5. ChildView ->onTouchEvent
  6. ParentView ->onTouchEvent
  7. Activity -> onTouchEvent

还不够清楚?如下图:

untitled.png

只要明白了以上的原理,就能根据不同的业务需求进行拦截等相关操作了。

最后有一些小知识点需要注意的:

  • 如果View设置了onTouchListener,会先执行OnTouchListeneronTouch方法。如果该方法返回true,表示消费了。不会执行onTouchEvent了,如果onTouch返回false,就会继续执行onTouchEvent

  • 如果View设置了OnClickListener的话,会在onTouchEvent中调用 listener.onClick(this);

  • 有一种场景,如果一个ViewGroup和它的childView都在onTouchEvent返回了false,根据上面的dispathchTouchEvent分发机制,事件会传到ViewGrouponTouchEvent,如果ViewGrouponTouchEvent也返回了false的话,就会返回到ActivityActivityonTouchEvent就会被调用。

结论

  • 一个事件的序列是从手指按下到手指抬起的整个过程,中间会有ACTION_DOWN,ACTION_MOVE,ACTION_UP的过程

  • 一个事件如果某个View拦截了,那么这个View会处理整个事件序列。

  • 如果View决定拦截某个事件的话,就会交给onTouchEvent处理,在一个事件序列中不会再调用onInterceptTouchEvent

  • 如果一个View的决定拦截某个事件(onInterceptTouchEvent return true),则在处理ACTION_DOWN的时候,如果返回false,表示他不处理这个事件,这时候dispatchTouchEvent就会返回false,那么后续事件就会交给上层View处理,就不会再调用这个ViewonTouchEvent了。也就是说后续的ACTION_MOVEACTION_UP都不会调用它的TouchEvent了,都交由父级View来处理。

  • ViewonTouchEvent默认返回trueViewlongClickableclickable都为false的时候就会返回true

  • childView可以通过requestDisallowInterceptTouchEvent()方法请求阻止或运行父View的事件分发过程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值