查看原文
其他

Android触摸事件分发机制完全解析《一》

2017-05-20 于亚豪 终端研发部


前言介绍


Android触摸事件分发机制完全解析

于亚豪的博客地址

正文

最近在做高德地图的时候,由于用户的要求,不得不用ScrollVew嵌套MapView,虽然很官方要求不建议这样做,但也迫于无奈... 魔高一尺,道高一丈.有什么事情是程序员不能解决的,如果有那就是解决两次.

鉴于用到了触摸事件,于是就来总结了Android的触摸事件机制.

首先当用户进行屏幕操作的时候,则有两种情况

  • 一是按键事件

  • 二是触摸事件

按键事件分为长按和点击事件,过于简单,这里不再进行总结.

触摸事件

触摸事件的组成:

  • 一个actionDown

  • n个actionMove

  • 一个actionUp

  • 一个onClick

  • 一个onLongClick

  • 一个onScroll

Android组件

  • 继承 ViewGroupo比如LinearLayout,ScrollView,GridView,extends-->ViewGroup的View

  • ViewGroup容器

  • 继承于 View不包含其他的View ,如TextView,Edittext,Butto等

下面介绍一个讲的好的一个博客地址:

MotionEvent

所有Touch事件都被封装成了MotionEvent对象,包括Touch的位置、时间、历史记录以及第几个手指(多指触摸)等。

Android 事件的处理的分类

  • 分发(很多人也称作为传递事件) : dispatchTouchEvent函数

  • 消费: onTouchEvent函数和OnTouchListener函数

  • 拦截:onInterceptTouchEvent函数

我们都知道Android的触摸事件都是从外层传递到内层:由最外层的Activity——>ViewGroup——> ViewGroupo——>.......View.

第一触摸事件传递的开始一定是Activity;

第二传递方式是通过隧道方式传递;

第三一直传递到一个最外层的View,也就是顶级View,由该View的这个方法来进行分发。

对于Activity来说:

@Override    public boolean dispatchTouchEvent(MotionEvent ev) {        
       boolean isFlag = super.dispatchTouchEvent(ev);        
       return isFlag;    }  
 
   @Override    public boolean onTouchEvent(MotionEvent event) {    
      boolean isFlag = super.onTouchEvent(event);      
      return isFlag;    }

对于ViewGroup来说:

@Override    public boolean onTouchEvent(MotionEvent event) {    
      boolean isFlag =super.onTouchEvent(event);      
        return  isFlag;    }    
        
   @Override    public boolean onInterceptTouchEvent(MotionEvent event) {    
      boolean isFlag =super.onInterceptTouchEvent(event);      
       return  isFlag;    }    
       
   @Override    public boolean dispatchTouchEvent(MotionEvent event) {      
        boolean isFlag =super.dispatchTouchEvent(event);    
        return  isFlag;    }

对于View来说和Activity一样,只有消费和拦截

   @Override    public boolean dispatchTouchEvent(MotionEvent ev) {      
     boolean isFlag = super.dispatchTouchEvent(ev);      
       return isFlag;    }  
       
    @Override    public boolean onTouchEvent(MotionEvent event) {  
        boolean isFlag = super.onTouchEvent(event);      
          return isFlag;    }

android中的Touch事件都是从ACTION_DOWN开始的:

单手指操作:ACTION_DOWN---ACTION_MOVE----ACTION_UP

多手指操作:ACTION_DOWN---ACTION_POINTER_DOWN---ACTION_MOVE--ACTION_POINTER_UP---ACTION_UP.

如果都不进行拦截,都不消费的基本流程图:

下面我们就用案例进行分析:

activity----》ViewGroup(MyLinearLayout)---》ViewGroup(MySubView)

MyLinearLayout的dispatchTouchEvent返回false。

打印结果:

  • 过程及结果分析:

  • 事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分发给 TouchEventFather 控件的dispatchTouchEvent;

  • 而该TouchEventFather 控件的 dispatchTouchEvent 返回 false,表示对获取到的事件停止向下传递,同时也不对该事件进行消费;

  • 由于 TouchEventFather 获取的事件直接来自 TouchEventActivity ,则会将事件返回给 TouchEventActivity 的 onTouchEvent 进行消费;

  • 最后直接由 TouchEventActivity 来响应手指移动和抬起事件。

MyLinearLayout的dispatchTouchEvent返回true。

打印结果:

  • 过程及结果分析:

  • 事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分发给 TouchEventFather 控件的 dispatchTouchEvent;

  • 而该TouchEventFather 控件的 dispatchTouchEvent 返回 true,表示分发事件到 TouchEventFather 控件并由该控件的 dispatchTouchEvent 进行消费;

  • 又因为TouchEventActivity 不断的分发事件到 TouchEventFather 控件的 dispatchTouchEvent,而 TouchEventFather 控件的 dispatchTouchEvent 也不断的将获取到的事件进行消费。

MyLinearLayout的onInterceptTouchEvent返回true。

打印结果:

  • 过程及结果分析:

  • 事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分发给 TouchEventFather 控件的 dispatchTouchEvent;

  • 而该TouchEventFather 控件的 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev),表示对事件进行分发并向下传递给 TouchEventFather 控件的 onInterceptTouchEvent 方法;

  • 而该方法返回 true 表示对所获取到的事件进行拦截并将事件传递给 TouchEventFather 控件的 onTouchEvent 进行处理,TouchEventFather 控件的 onTouchEvent 返回 super.onTouchEvent(ev) 表示对事件没有做任何处理直接将事件返回给上级控件;

  • 由于 TouchEventFather 获取的事件直接来自 TouchEventActivity,所以 TouchEventFather 控件的 onTouchEvent 会将事件以冒泡方式直接返回给 TouchEventActivity 的 onTouchEvent 进行消费;

  • 后续的事件则会跳过 TouchEventFather 直接由 TouchEventActivity 的 onTouchEvent 消费来自 TouchEventActivity 自身分发的事件。

MyLinearLayout的onInterceptTouchEvent返回false。

打印结果:

  • 过程及结果分析:

  • 事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分发给 TouchEventFather 控件的 dispatchTouchEvent;

  • 而该控件的 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev),表示对事件进行分发并向下传递给 TouchEventFather 控件的 onInterceptTouchEvent 方法;

  • 该方法返回 false 表示事件会被放行并传递到子控件 TouchEventChilds 的 dispatchTouchEvent 方法;

  • 同样 TouchEventChilds 的 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev),表示对事件进行分发并向下传递给 TouchEventChilds 控件的 onInterceptTouchEvent 方法;

  • 而TouchEventChilds 的 onInterceptTouchEvent 方法返回 super.onInterceptTouchEvent(ev) ,默认会将事件传递给 TouchEventChilds 的 onTouchEvent 进行处理;

  • 而TouchEventChilds 的 onTouchEvent 返回 super.onTouchEvent(ev) 表示对事件没有做任何处理直接将事件返回给上级控件;

  • 由于 TouchEventChilds 获取的事件直接来自 TouchEventFather,所以 TouchEventChilds 控件的 onTouchEvent 会将事件以冒泡方式直接返回给 TouchEventFather 的 onTouchEvent 进行消费;

  • 而 TouchEventFather 的 onTouchEvent 也返回了 super.onTouchEvent(ev),同样 TouchEventFather 的 onTouchEvent 也会将事件返回给上级控件;

  • 而 TouchEventFather 获取的事件直接来自 TouchEventActivity,所以 TouchEventFather 控件的 onTouchEvent 会将事件以冒泡方式直接返回给 TouchEventActivity 的 onTouchEvent 进行消费;

  • 后续的事件则会跳过 TouchEventFather 和 TouchEventChilds 直接由 TouchEventActivity 的 onTouchEvent 消费来自 TouchEventActivity 自身分发的事件。

MyLinearLayout的onInterceptTouchEvent返回false。MySubView返回true

打印结果:

  • 过程及结果分析:

  • 事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分发给 TouchEventFather 控件的 dispatchTouchEvent;

  • 该控件的 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev),事件会分发到 TouchEventFather 的 onInterceptTouchEvent,此方法返回 false 表示放行当先事件;

  • 事件会被传递到子控件 TouchEventChilds 的 dispatchTouchEvent 方法,dispatchTouchEvent 返回 true 表示事件被分发到 TouchEventChilds ,并由 dispatchTouchEvent 方法消费;

  • 后续的事件也会不断的重复上面的逻辑最终被 TouchEventChilds 的 dispatchTouchEvent 消费。

其实如果看源码的话比较复杂,总结一下,基本的规则是:

  • down事件首先会传递到onInterceptTouchEvent()方法

  • 如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return false,内部view将也会获取到down,那么后续的move, up等事件将继续会先传递给该ViewGroup,之后一样传递给最终的目标view的onTouchEvent()处理。

  • 如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return true,内部view不会货到down,后续的move, up等事件也不再传递给onInterceptTouchEvent(),当然,move,up也不会传给view

  • 如果最终需要处理事件的view的onTouchEvent()返回了false,那么该事件将被传递至其上一层次的view的onTouchEvent()处理。

  • 如果最终需要处理事件的view 的onTouchEvent()返回了true,那么后续事件将可以继续传递给该view的onTouchEvent()处理。

  • group的touch和这个机制没关系,与上一级有关系。

来自androidstarjack博客地址:


让心,在阳光下学会舞蹈

让灵魂,在痛苦中学会微笑

—终端研发部—



如果你觉得此文对您有所帮助,欢迎入群 QQ交流群 :232203809   

微信公众号:终端研发部

(欢迎关注学习和交流)




            

Hello,伙伴们

长按二维码就可以关注我们啦




(欢迎关注学习和交流)


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存