30分钟搞清楚Android Touch事件分发机制

30分钟搞清楚Android Touch事件分发机制,第1张

概述Touch事件分发中只有两个主角:ViewGroup和View。Activity的Touch事件事实上是调用它内部的ViewGroup的Touch事件,可以直接当成ViewGroup处理。

touch事件分发中只有两个主角:VIEwGroup和VIEw。Activity的touch事件事实上是调用它内部的VIEwGroup的touch事件,可以直接当成VIEwGroup处理。

VIEw在VIEwGroup内,VIEwGroup也可以在其他VIEwGroup内,这时候把内部的VIEwGroup当成VIEw来分析。

VIEwGroup的相关事件有三个:onIntercepttouchEvent、dispatchtouchEvent、ontouchEvent。VIEw的相关事件只有两个:dispatchtouchEvent、ontouchEvent。

先分析VIEwGroup的处理流程:首先得有个结构模型概念:VIEwGroup和VIEw组成了一棵树形结构,最顶层为Activity的VIEwGroup,下面有若干的VIEwGroup节点,每个节点之下又有若干的VIEwGroup节点或者VIEw节点,依次类推。如图:

当一个touch事件(触摸事件为例)到达根节点,即Acitivty的VIEwGroup时,它会依次下发,下发的过程是调用子VIEw(VIEwGroup)的dispatchtouchEvent方法实现的。简单来说,就是VIEwGroup遍历它包含着的子VIEw,调用每个VIEw的dispatchtouchEvent方法,而当子VIEw为VIEwGroup时,又会通过调用ViwGroup的dispatchtouchEvent方法继续调用其内部的VIEw的dispatchtouchEvent方法。上述例子中的消息下发顺序是这样的:①-②-⑤-⑥-⑦-③-④。dispatchtouchEvent方法只负责事件的分发,它拥有boolean类型的返回值,当返回为true时,顺序下发会中断。在上述例子中如果⑤的dispatchtouchEvent返回结果为true,那么⑥-⑦-③-④将都接收不到本次touch事件。来个简单版的代码加深理解:
 

/**   * VIEwGroup   * @param ev   * @return   */  public boolean dispatchtouchEvent(MotionEvent ev){    ....//其他处理,在此不管    VIEw[] vIEws=getChildVIEw();    for(int i=0;i<vIEws.length;i++){      //判断下touch到屏幕上的点在该子VIEw上面       if(...){      if(vIEws[i].dispatchtouchEvent(ev))       return true;       }    }    ...//其他处理,在此不管  }  /**   * VIEw   * @param ev   * @return   */  public boolean dispatchtouchEvent(MotionEvent ev){    ....//其他处理,在此不管    return false;  }

在此可以看出,VIEwGroup的dispatchtouchEvent是真正在执行“分发”工作,而VIEw的dispatchtouchEvent方法,并不执行分发工作,或者说它分发的对象就是自己,决定是否把touch事件交给自己处理,而处理的方法,便是ontouchEvent事件,事实上子VIEw的dispatchtouchEvent方法真正执行的代码是这样的

/**   * VIEw   * @param ev   * @return   */  public boolean dispatchtouchEvent(MotionEvent ev){    ....//其他处理,在此不管    return ontouchEvent(event);  }

一般情况下,我们不该在普通VIEw内重写dispatchtouchEvent方法,因为它并不执行分发逻辑。当touch事件到达VIEw时,我们该做的就是是否在ontouchEvent事件中处理它。

那么,VIEwGroup的ontouchEvent事件是什么时候处理的呢?当VIEwGroup所有的子VIEw都返回false时,ontouchEvent事件便会执行。由于VIEwGroup是继承于VIEw的,它其实也是通过调用VIEw的dispatchtouchEvent方法来执行ontouchEvent事件。

 

在目前的情况看来,似乎只要我们把所有的ontouchEvent都返回false,就能保证所有的子控件都响应本次touch事件了。但必须要说明的是,这里的touch事件,只限于Acition_Down事件,即触摸按下事件,而Aciton_UP和Action_MOVE却不会执行。事实上,一次完整的touch事件,应该是由一个Down、一个Up和若干个Move组成的。Down方式通过dispatchtouchEvent分发,分发的目的是为了找到真正需要处理完整touch请求的VIEw。当某个VIEw或者VIEwGroup的ontouchEvent事件返回true时,便表示它是真正要处理这次请求的VIEw,之后的Aciton_UP和Action_MOVE将由它处理。当所有子VIEw的ontouchEvent都返回false时,这次的touch请求就由根VIEwGroup,即Activity自己处理了。

看看改进后的VIEwGroup的dispatchtouchEvent方法

VIEw mTarget=null;//保存捕获touch事件处理的VIEw  public boolean dispatchtouchEvent(MotionEvent ev) {    //....其他处理,在此不管        if(ev.getAction()==KeyEvent.ACTION_DOWN){      //每次Down事件,都置为Null      if(!onIntercepttouchEvent()){      mTarget=null;      VIEw[] vIEws=getChildVIEw();      for(int i=0;i<vIEws.length;i++){        if(vIEws[i].dispatchtouchEvent(ev))          mTarget=vIEws[i];          return true;      }     }    }    //当子VIEw没有捕获down事件时,VIEwGroup自身处理。这里处理的touch事件包含Down、Up和Move    if(mTarget==null){      return super.dispatchtouchEvent(ev);    }    //...其他处理,在此不管    if(onIntercepttouchEvent()){     //...其他处理,在此不管       }//这一步在Action_Down中是不会执行到的,只有Move和UP才会执行到。    return mTarget.dispatchtouchEvent(ev);  }

VIEwGroup还有个onIntercepttouchEvent,看名字便知道这是个拦截事件。这个拦截事件需要分两种情况来说明:

1.假如我们在某个VIEwGroup的onIntercepttouchEvent中,将Action为Down的touch事件返回true,那便表示将该VIEwGroup的所有下发 *** 作拦截掉,这种情况下,mTarget会一直为null,因为mTarget是在Down事件中赋值的。由于mTarge为null,该VIEwGroup的ontouchEvent事件被执行。这种情况下可以把这个VIEwGroup直接当成VIEw来对待。

2.假如我们在某个VIEwGroup的onIntercepttouchEvent中,将Acion为Down的touch事件都返回false,其他的都返回True,这种情况下,Down事件能正常分发,若子VIEw都返回false,那mTarget还是为空,无影响。若某个子VIEw返回了true,mTarget被赋值了,在Action_Move和Aciton_UP分发到该VIEwGroup时,便会给mTarget分发一个Action_Delete的MotionEvent,同时清空mTarget的值,使得接下去的Action_Move(如果上一个 *** 作不是UP)将由VIEwGroup的ontouchEvent处理。

情况一用到的比较多,情况二个人还未找到使用场景。

从头到尾总结一下:

1.touch事件分发中只有两个主角:VIEwGroup和VIEw。VIEwGroup包含onIntercepttouchEvent、dispatchtouchEvent、ontouchEvent三个相关事件。VIEw包含dispatchtouchEvent、ontouchEvent两个相关事件。其中VIEwGroup又继承于VIEw。

2.VIEwGroup和VIEw组成了一个树状结构,根节点为Activity内部包含的一个ViwGroup。

3.触摸事件由Action_Down、Action_Move、Aciton_UP组成,其中一次完整的触摸事件中,Down和Up都只有一个,Move有若干个,可以为0个。

4.当Acitivty接收到touch事件时,将遍历子VIEw进行Down事件的分发。VIEwGroup的遍历可以看成是递归的。分发的目的是为了找到真正要处理本次完整触摸事件的VIEw,这个VIEw会在ontouchuEvent结果返回true。

5.当某个子VIEw返回true时,会中止Down事件的分发,同时在VIEwGroup中记录该子VIEw。接下去的Move和Up事件将由该子VIEw直接进行处理。由于子VIEw是保存在VIEwGroup中的,多层VIEwGroup的节点结构时,上级VIEwGroup保存的会是真实处理事件的VIEw所在的VIEwGroup对象:如VIEwGroup0-VIEwGroup1-TextVIEw的结构中,TextVIEw返回了true,它将被保存在VIEwGroup1中,而VIEwGroup1也会返回true,被保存在VIEwGroup0中。当Move和UP事件来时,会先从VIEwGroup0传递至VIEwGroup1,再由VIEwGroup1传递至TextVIEw。

6.当VIEwGroup中所有子VIEw都不捕获Down事件时,将触发VIEwGroup自身的ontouch事件。触发的方式是调用super.dispatchtouchEvent函数,即父类VIEw的dispatchtouchEvent方法。在所有子VIEw都不处理的情况下,触发Acitivity的ontouchEvent方法。

7.onIntercepttouchEvent有两个作用:1.拦截Down事件的分发。2.中止Up和Move事件向目标VIEw传递,使得目标VIEw所在的VIEwGroup捕获Up和Move事件。
 补充:

“触摸事件由Action_Down、Action_Move、Aciton_UP组成,其中一次完整的触摸事件中,Down和Up都只有一个,Move有若干个,可以为0个。”,这里补充下其实UP事件是可能为0个的。
以上就是本文的全部内容,希望对大家理解touch事件分发机制有所帮助。

总结

以上是内存溢出为你收集整理的30分钟搞清楚Android Touch事件分发机制全部内容,希望文章能够帮你解决30分钟搞清楚Android Touch事件分发机制所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/1149620.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-31
下一篇 2022-05-31

发表评论

登录后才能评论

评论列表(0条)

保存