欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android事件分发机制

程序员文章站 2022-05-14 15:23:46
...

在事件分发过程中有三个非常重要的方法:

// 该方法用于事件分发,在每次事件中该方法一定会被调用,返回值表示是否拦截此次事件,返回值收到onIterceptTouchEvent和onTouchEvent影响.
public boolean dispathTouchEvent(MotionEvent ev )

// 该方法用于判断是否拦截事件,当当前View对某一时间进行了拦截,则该事件序列下,其onIterceptTouchEvent方法则不会再被调用,返回值表示是否拦截此次事件
public boolean onIterceptTouchEvent(MotionEvent ev )

//该方法用于对拦截事件的具体处理,当onIterceptTouchEvent返回true会调用该方法,若返回false则不会被调用,该方法的返回值表示是否消耗当前事件
public boolean onTouchEvent(MotionEvent ev )

以上上三个函数的关系可以用如下的伪代码进行表示:


public boolean dispathTouchEvent(MotionEvent ev ){
    boolena comsume = false;
    //当onIterceptTouchEvent返回true会调用onTouchEvent方法
    if(onIterceptTouchEvent(ev)){
     comsume = onTouchEvent(ev);   
    }else{
    //若onIterceptTouchEvent返回false则调用子元素的dispathTouchEvent方法,将该事件进行进一步的传递
     comsume = child.dispathTouchEvent(ev);   
    }

    return comsume;
}

事件分发的整体流程:

所有的点击事件都会先传递给activity,activity会再将该事件传递给Window,Window会再传递给底层容器(DecorView),这个容器(DecorView)是专门用于装载我们设定的视图,例如我们setContentView设定的布局,然后DecorView会将事件传递给View,进行事件分发的逻辑.

其中值得一说的是,如果一子元素的onTouchEvent返回false,则会调用其父类的onTouchEvent方法,依照递推,当所有的View返回的都是false的时候,activity的onTouchEvent会被调用,如下图所示:

Android事件分发机制


事件分发机制概述:

对一个ViewGroup来说,当发生一个点击事件后,首先它的dispathTouchEvent方法会被调用来进行该事件的分发,其受onInterceptTouchEvent是否拦截的影响,当onInterceptTouchEvent返回true,表示拦截该事件,随后会调用onTouchEvent方法进行具体的拦截后的逻辑处理,当onInterceptTouchEvent返回false,表示不对该事件进行拦截,则onTouchEvent不会被调用,转而继续传递给它的子元素,调用子元素的dispathTouchEvent方法,进行新一轮的事件分发.

onTouchEvent与onTouch,onClick:

在事件分发过程中,处理拦截的View,其内部逻辑首先会判断是否对onTouchListener进行了实现,当进行了实现时,若onTouch返回了true,则onTouchEvent方法则不会再被调用,若onTouch返回了false,则onTouchEvent方法则会被继续调用,从这部分逻辑中可以看出onTouch的优先级高于onTouchEvent,若在onTouchEvent中对View的onCLickListener进行了实现则会调用其onClick方法.

所以三者的优先级关系为:onTouch > onTouchEvent > onClick


ViewGroup对事件分发的处理:

ViewGroup对事件是否拦截,取决于两点:
1. 该事件是否为ACTION_DOWN
2. mfirstTouchTarget是否为null

Android事件分发机制

在上图为源码,判断ACTION_DOWN好理解,而mFirstTouchTarget是一个单项列表,用于存储指向最终消费时间的子元素,在实际情况中,实际消费的View之上有很多层,而mFirstTouchTarget就是一层一层的指向,最终指向消费事件的View.
当mfirstTouchEvent不为空,那么就说明其子元素已经对该事件进行了消费,进而不进行调用onInterceptTouchEevnt


ViewGroup对子元素进行事件分发处理:

ViewGroup对子元素遍历的顺序逻辑如下图:

Android事件分发机制

在遍历的过程中也会判断,该子元素是否能够接收到点击事件,判断依据两点:1.是否在播放动画;2.坐标是否坐落在点击范围内;当满足这两点条件的时候就会直接调用子元素的dispathTouchEvent.

当有子元素的dispathTouchEvent返回为true的时候,就会跳出循环,并且将该View存入到mFirstTouchTarget单项链表中.


事件分发的归纳总结:

  1. 一个完成的事件序列,是从手指接触屏幕的时候开始,到手指离开屏幕结束,对应的事件是从Down事件开始,中间有不定数量的Move,最终以Up结束为一个完整的事件序列.
  2. 通常情况下一个事件序列只会被一个View处理,特殊情况下就不一定是这样的,比如在TouchEvent事件中,强制传递给其他View
  3. 当一个View拦截某一事件,那么接下来的一整个事件序列都会交给该View来处理
  4. 当一个View拦截某一事件,其onInterceptTouchEvent函数就不会再被调用,因为系统已经将一个事件序列交给了该View处理,也就没有必要调用onIterceptTouchEvent判断是否拦截的必要
  5. 一个View不处理除ACTION_DOWN以外的事件,该点击事件会消失,其父类也不会调用onTouchEvent事件,并且该View还会继续接受到后续的事件,而消失的事件会传递activity处理
  6. ViewGroup默认不拦截事件,在源码的onInterceptTouchEvent默认返回值为false
  7. View没有onInterceptTouchEvent函数,一旦有事件传递给它,就会直接调用onTouchEvent方法
  8. View的onTouchEvent都默认返回为true,(除非它的clickable和longClickable都未false),通常longClickable都默认为false,而clickable看情况,Button的clickable默认为true,而TextView默认为false.
  9. enable属性不影响onTouchEvent事件,哪怕enable为disable也不受影响,只要clickable和longClickable有一个属性为true,那么onTouchEvent就会返回为true
  10. onClick点击事件,需要满足两个大前提,一,该View可以点击;二,并且接受到了Down和Up事件
  11. requestDisallowInterceptTouchEvent可以在子元素中影响父元素的事件分发,父元素无法拦截ACTION_DOWN以外的事件

requestDisallowInterceptTouchEvent为什么无法拦截ACTION_DOWN以外的事件

在ACTION_DOWN事件中,会重置FLAG_DISALLOW_INTERCEPT标记,会导致子元素中对该标记的使用失效.ViewGroup会在ACTION_DOWN到来时进行重置状态操作.

相关标签: 事件分发