一、简介

或许你会问,“为什么我一定要知道View的事件分发机制?”。因为我们在实际开发的过程中,经常会遇到多层的View互相嵌套以后,对某一个View进行滑动的时候,特别不灵敏,甚至于没法滑动。这种滑动冲突的解决需要我们清楚的掌握View的事件分发机制。那下面我们详细的讲解下View的整个事件机制。
Android将View的事件封装到MotionEvent这个类中,这也是监听touch事件中回调给我们的参数public boolean onTouchEvent(MotionEvent event) 。通常事件我们主要关心下面几种类型:

  • MotionEvent.ACTION_DOWN
    当我们手指按下屏幕的第一个事件便是ACTION_DOWN了,也就是意味着事件的开始。

  • MotionEvent.ACTION_MOVE
    当我们手指按下屏幕后,在屏幕上滑动的过程,此事件就会不断的触发。

  • MotionEvent.ACTION_UP
    此事件在我们手指从屏幕抬起的时候会触发。

  • MotionEvent.ACTION_CANCEL
    这个事件说起来稍微复杂一点,举个栗子:当我们的外层View将事件传递给内层View去处理时,外层View的拦截方法一般会返回false,但是当某个条件触发后,外层View想自己处理接下来的事件,就拦截了事件分发,此时内层View就会收到ACTION_CANCEL的事件。

  • MotionEvent.ACTION_OUTSIDE
    这个事件我们不常用到,考虑这种场景。我们又一个Diallog弹出,当我们按Dialog以外的屏幕将Dialog消失掉。这个时候可以考虑监听这个事件,要想使用这个事件我们必须对当前的Window设置一个Flag:FLAG_WATCH_OUTSIDE_TOUCH

下面我们介绍和事件分发相关的几个方法:

  • dispatchTouchEvent(MotionEvent event)
    这个方法是用来处理向下分发事件逻辑的,我们通过观察ViewGrope源码中的代码知道,这个方法细节较多,检出我们比较关心的逻辑就是这个方法会先判断子View是否有调用disallowIntercept父View去拦截事件,如果没有,父View自己会调用onInterceptTouchEvent判断自己是否有拦截,如果拦截事件,将调用父View自己的onTouchEvent方法去处理事件,如果没有拦截事件,事件将继续分发到子View中处理。

  • onInterceptTouchEvent(MotionEvent event)
    用来申明是否拦截事件继续向下分发,如果返回true,事件将不会继续向下分发,而是交由自己的onTouchEvent方法处理。

  • onTouchEvent(MotionEvent event)
    显然,这个就是事件处理的方法了。

  • onTouch(MotionEvent event)
    这个方法是在我们对某一个setOnTouchListener时回调,也就是在传递事件的时候,在交给View本身的onTouchEvent处理之前判断是否有监听的TouchListener,如果有优先调用TouchListener的onTouch方法处理。

二、详细分析View的分发事件

我们都知道,Android的View是树形结构的,所以当一个事件来临的时候一般是从根部分发下来的。为了方便我们接下来的理解,我们构建一个这样的例子:

假设我们有这样一个页面,最外层是一个ViewGroup A,里面嵌套着一个ViewGroup B,B里面有一个ViewGroup C。

情景1:

假设我们对事件不做任何拦截,也不做任何处理。当我们点击View C,这个时候我们看到的Log显示调用顺序为:

A -> dispatchTouchEventA -> onInterceptTouchEventB -> dispatchTouchEventB -> onInterceptTouchEventC -> dispatchTouchEventC -> onInterceptTouchEventC -> onTouchEvent ACTION_DOWNB -> onTouchEvent ACTION_DOWNA -> onTouchEvent ACTION_DOWN

由于没有任何View处理事件,最终会回调到Activity的onTouchEvent中去处理。从这个情景中我们可以知道,事件向下传递的过程以及处理事件的向上传递的过程。

情景2:

假设我们在View BonTouchEvent中返回true,再次点击事件并滑动,我们得到的Log如下:

A -> dispatchTouchEventA -> onInterceptTouchEventB -> dispatchTouchEventB -> onInterceptTouchEventC -> dispatchTouchEventC -> onInterceptTouchEventC -> onTouchEvent ACTION_DOWNB -> onTouchEvent ACTION_DOWNA -> dispatchTouchEventA -> onInterceptTouchEventB -> dispatchTouchEventB -> onTouchEvent ACTION_MOVEA -> dispatchTouchEventA -> onInterceptTouchEventB -> dispatchTouchEventB -> onTouchEvent ACTION_UP  

我们发现,除了ACTION_DOWN事件会下发到C,后续的事件不会再下发这是因为,当我们发现某一层View的onTouchEvent返回true以后,会有一个标志位表示后续的事件都由此View处理,后续事件不再下发到子View,直到ACTION UP事件后将标志位重置。

情景3:

假设我们在View B的onInterceptTouchEvent中返回true,再次点击C会怎么样呢?我们得到如下的Log记录:

A -> dispatchTouchEventA -> onInterceptTouchEventB -> dispatchTouchEventB -> onInterceptTouchEventB -> onTouchEvent ACTION_DOWNA -> onTouchEvent ACTION_DOWN

相比较于情景2,ACTION_DOWN事件不会下发到C,由于没有View表示能处理,所以后续的事件均被取消。

三、总结

通过我们实际运行和分析源码发现,我们ViewGroup事件的分发流程如下所示:

对着上图大家不妨尝试分析下:

  • 如果B的onInterceptTouchEvent中返回true并且onTouchEvent中返回true,那么Log又将是怎样的呢?

可以关注微信公众号:南京Android部落,回复“666”获取答案哦~

更多相关文章

  1. Android(安卓)开发之实时更新 App Widget
  2. Android(安卓)监听短信(同时监听广播和数据库)
  3. 让android的webview中的按钮,触发事件,也能像原生按钮一样使用
  4. Android中的双击事件
  5. Android(安卓)开发事件响应之基于监听的事件响应
  6. Android(安卓)viewpager嵌套viewpager滑动冲突的解决
  7. Android(安卓)上从外部应用注入按键事件流程分析
  8. 从源码剖析PopupWindow 兼容Android(安卓)6.0以上版本点击外部不
  9. Android(安卓)view触摸反馈原理和源码分析

随机推荐

  1. Android(安卓)使用WebView加载含有echart
  2. Android架构分析之Android智能指针(二)
  3. Android(安卓)最火的快速开发框架XUtils
  4. Android(安卓)Project from Existing Cod
  5. Android图形显示系统(一)
  6. Ubuntu for Android:共享同一linux内核,运
  7. Android的logcat用法
  8. Android(安卓)Studio中Xml的新建及shape
  9. ant script to obfuscate android code
  10. Android中的动画详解系列【2】——飞舞的