View 的事件分发机制

说到事件分发机制,这也是一道坎,源码不可谓不复杂,但整个代码思路确实清晰的,下面一起分析一下把。

三个方法

  • public boolean dispatchTouchEvent(MotionEvent ev) :事件分发
  • public boolean onInterceptTouchEvent(MotionEvent ev):事件拦截
  • public boolean onTouchEvent(MotionEvent ev):事件响应

它们都会返回一个boolean 值,然后我们来分析一下每个方法的布尔值到底代表生命意思。这样才能更好的理解:

public boolean dispatchTouchEvent(MotionEvent ev) 

准确的说 它的返回值是不是最先得到的,要经过activity ,viewGruop ,view 对事件处理的结果来看的,也就是说它的值是由别人控制的,并不是单独的一个独立方法,我们可以看看下面代码

public boolean dispatchTouchEvent(MotionEvent ev){
    //默认返回值
    boolean consume = false;

    //如果事件发生了拦截
    if(onInterceptTouchEvent(ev)){
        //消费事件
        consume = onTouchEvent(ev);
    }else{
        //否则分发给子View
        consume = child.dispatchTouchEvent(ev);
    }

    //返回值
    return consume;
}

描述了三个方法的关系,事件传递的关系。所以说在得到dispatchTouchEvnet的结果依赖另外两个方法,并且我们知道事件传递是一个U 型过程,从上到下,在从下到上。(以前老是想一开始就根据dispathTouchEvent的值判断事件消费的情况,和另外两个没什么关系,其实这是错误的)

public boolean onInterceptTouchEvent(MotionEvent ev):事件拦截

  • 如果return true,则表示将事件进行拦截,并将拦截到的事件交由当前 View 的 onTouchEvent 进行处理;

  • 如果return false,则表示将事件放行,当前 View 上的事件会被传递到子 View 上,再由子 View 的 dispatchTouchEvent 来开始这个事件的分发;

注意的是这个方法在viewGruop 和view 区分对待,因为view 没有此方法(此时把该view看作view tree最底层的树,它没有子view了,不需要分发),另外,系统viewGruop 默认对此方法返回false,交给子view去处理。

public boolean onTouchEvent(MotionEvent ev):事件响应(是否消费事件)

  • 如果return true 则会接收并消费该事件。

  • 如果return false,表示不消费。此时,我们应该结合事件分发机制的图来看,若是最底层的view返回false,则该事件会向上传递,即其父view的onTouchEvent 事件被触发,一直到acitivity,若acitivity的onTouchEvent()也不处理,该事件就会消失,无效。假如是事件从上往下的过程中,onTouchEvent() 返回了false,一定是它的 onIntercepetTouchEvent 返回了true拦截了该事件,这时候这个事件就会消失。

再来看一个表格,这个表格很关键

对照这个表格,再看看上面的结论就比较清晰了,onInterceptTouchEvent()  在viewGruop和view要分开来看,原因就在这里。

相应结论:

  • 事件分发过程由dispatchTouchEvent函数开始,但重点是onInterceptTouchEvent  和 onTouchEvent ,而且这两个方法决定了前者的值;
  • 事件分发经过 acitivity---- window(PhoneWindow)---DecorView----ViewGroup----view ,若view不处理会依次原路返回经过它们的onTouchEvent()函数。
  • 事件分发时,进入到 dispatchTouchEvent()方法中后,viewGroup 会先判断自己要不要拦截 onInterceptTouchEvent() ,如果要拦截,就会给自己的onTouchEvent, 系统的viewGruop默认返回false,自定义的viewGroup可以让其返回true,同时,view的onTouchEvent() 默认返回true ,除非 clickable 和longClickable都为false,例如button的为true,textView为false。这个和之前总结的TV 应用的app 的onSelect 属性类似。
  • requestDisallowInterceptTouchEvent(true),表示不允许父元素拦截事件,这样事件就会传递给子View。一般这个方法子View用的多,可以用来处理滑动冲突问题。
     

事件冲突解决

外部冲突解决:事件都先经过父容器拦截处理,需要就拦截,不需要就不拦截;

action_down:不能拦截,因为一旦拦截了它,所有事件都归自己处理了;

action_move:按需实现

action_up: 不拦截,返回false

内部冲突解决:父view默认不拦截任何事件,所有事件给子view,子view不需要的交由父view处理(实现方式就是requestDisallowInterceptTouchEvent(true))。

action_down:父view不能拦截该方法,所以子view调用 requestDisallowInterceptTouchEvent(true),

action_move: 子view调用 requestDisallowInterceptTouchEvent(false) ,让父view处理相应的逻辑;

参考文章:https://www.cnblogs.com/fang1019/p/7613946.html

Android开发艺术探讨

猜你喜欢

转载自blog.csdn.net/sjh_389510506/article/details/88649852
今日推荐