android 的触摸事件的分发拦截机制分析

时间:2022-10-02 22:33:26
   

       今天给大家讲解一下android中触摸事件的分发拦截机制,Android为触摸事件封装了一个类,MotionEvent,如果在项目中你重写过onTouchEvent方法,你就会发现该参数就是一个MotionEvent。在MotionEvent中封装了不少比较实用的东西,比如触摸点的坐标,点击事件的类别等。这样大家就对触摸事件有了一定的了解了,现在说一下事件的拦截机制。

      
我们大家都知道,Android的view结构是树形结构。View可以放在ViewGroup里面,通过不同的组合来实现不同的样式和嵌套。可是我们的触摸事件就一个,应该分给谁呢,这时候就需要事件拦截机制的出现。为了初学者更好的理解它,给大家举一个日常中的场景:假设你所在的公司里,有一个总经理,级别最高。他的下面有一个部长,级别次之。在最底层,就是干活的你。现在董事会交给总经理一项任务,总经理将这项任务布置给了部长,部长又把任务安排给了你,当你干完活之后,你就把任务交给了部长,部长觉得完成的可以,签上他的名字交给了总经理,总经理也觉得不错就签上字交给了董事会。这样,一个任务就顺利完成了,这就是一个完整的事件传递机制。


在上面的场景中,总经理相当于MyViewGroupA,最外层的ViewGroup;部长相当于MyViewGroupB,中间的ViewGroup;干活的你就是最底层的MyView。


在上面给大家举例的是典型的较简单的类型,下面按照上面给大家进行详细的讲解。

在事件传递中需要重写的方法有:dispatchTouchEvent(MotionEvent e),onInterceptTouchEvent(MotionEvent e),onTouchEvent(MotionEvent e)。


上面是相对于ViewGroup来说,对于View来说,只需要重写onTouchEvent(MotionEvent e)和dispatchTouchEvent(MotionEvent e)


从上面可以看出ViewGroup比View多一个onInterceptTouchEvent(MotionEvent e)方法,这个方法就是事件拦截机制的核心。

像下面一样,给每个方法添加注释

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d("xys","ViewGroupA dispatchTouchEvent"+ev.getAction());
return super.dispatchTouchEvent(ev);
}


@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {

Log.d("xys","ViewGroupA InterceptTouchEvent"+ev.getAction());
return super.onInterceptTouchEvent(ev);
}

@Override
public boolean onTouchEvent(MotionEvent event) {

Log.d("xys","ViewGroupA onTouchEvent"+event.getAction());
return super.onTouchEvent(event);
}

在Logcat中打印的log是:

ViewGroupA dispatchTouchEvent

ViewGroupA onInterceptTouchEvent

ViewGroupB dispatchTouchEvent

ViewGroupB onInterceptTouchEvent

View dispatchTouchEvent

View onTouchEvent

从上面的打印信息可以看到,正常情况下,事件传递的顺序是:

总经理(ViewGroupA)->部长(ViewGroupB)->你(View)。事件传递的时候,先执行dispatchTouchEvent(),然后执行onInterceptTouchEvent()

事件的处理顺序:

你(View)->部长(M有ViewGroupB)->总经理(MyGroupViewA)。事件的处理都是执行onTouchEvent()方法。



事件传递的返回值非常容易理解:true,拦截,不继续传递;false,不拦截,继续传递。

事件处理的返回值:true,处理,不用审核;false,给上级处理。


在事件传递中,我们只关心onInterceptTouchEvent()方法,dispatchTouchEvent()方法虽然是事件分发的第一步,但我们不太会去改写这个方法,所以暂时不管。


还是上面的例子,如果MyGroupViewA的onInterceptTouchEvent()方法返回True,再看一下log.

ViewGroupA dispatchTouchEvent

ViewGroupA onInterceptTouchEvent

ViewGroupA onTouchEvent

通过上面我们会总结如下:

如果这个ViewGroup的onIterceptTouchEvent返回true就表示它要拦截当前事件,接着这个ViewGroup的onTouchEvent就会被调用.如果onIterceptTouchEvent返回false,那么就会继续向下调用子View的dispatchTouchEvent方法

还有就是:当一个View需要处理事件的时候,如果它没有设置onTouchListener,那么直接调用onTouchEvent.如果设置了Listenter 那么就要看Listener的onTouch方法返回值.为true就不调,为false就调onTouchEvent;View的onTouchEvent默认都会消耗事件,除非它的clickable和longClickable都是false(不可点击),但是enable属性不会影响




综上所述,android事件分发流程如下:

  1. 事件都是从Activity.dispatchTouchEvent()开始传递
  2. 事件由父View传递给子View,ViewGroup可以通过onInterceptTouchEvent()方法对事件拦截,停止其向子view传递
  3. 如果事件从上往下传递过程中一直没有被停止,且最底层子View没有消费事件,事件会反向往上传递,这时父View(ViewGroup)可以进行消费,如果还是没有被消费的话,最后会到Activity的onTouchEvent()函数。
  4. 如果View没有对ACTION_DOWN进行消费,之后的其他事件不会传递过来,也就是说ACTION_DOWN必须返回true,之后的事件才会传递进来
  5. OnTouchListener优先于onTouchEvent()对事件进行消耗


好了,今天的事件拦截和分发就讲到这里吧。