Android 触摸事件分发过程

前言

这是demo 链接

Android 中 View 的事件传递是一个老生常谈的问题,但也是学习 Android 的重点和难点。本人将分三篇来分享 Android 中的事件传递。

  • 通过 demo 分析 Android 事件的传递过程
  • 通过源码分析 Android 事件的传递过程
  • 运用所学解决 Android 中滑动冲突

MotionEvent

Android 中事件的传递其实传递的就是 MotionEvent 对象。MotionEvent 中封装了事件的各种状态和属性。最常用的最典型的有如下几种:

  • ACTION_DOWN ———————手指刚接触屏幕
  • ACTION_MOVE ———————手指在屏幕上移动
  • ACTION_UP ———————手指从屏幕上松开的一瞬间

Android 事件传递相关方法

  • public boolean dispatchTouchEvent(MotionEvent ev) 负责事件的分发
  • public boolean onInterceptTouchEvent(MotionEvent ev) 负责事件拦截
  • public boolean onTouchEvent(MotionEvent event) 负责事件的处理

在 ViewGroup 中有上述三个方法,但是在 View 中没有 onInterceptTouchEvent 方法。因为 View 没有子 View,所以就不牵扯到事件的拦截。

编写 demo

自定义相关 View

由于 LinearLayout 继承 ViewGroup,我们只需新建类 CustomLinearLayout 继承 LinearLayout。重写上述三个方法,如下

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
      switch (ev.getAction()){
        case MotionEvent.ACTION_DOWN:
          Log.i(TAG, "======dispatchTouchEvent======ACTION_DOWN==");
        break;
        case MotionEvent.ACTION_MOVE:
          Log.i(TAG, "======dispatchTouchEvent======ACTION_MOVE==");
        break;
        case MotionEvent.ACTION_UP:
          Log.i(TAG, "======dispatchTouchEvent======ACTION_UP==");
        break;
      }
      return super.dispatchTouchEvent(ev);} 

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
      switch (ev.getAction()){
        case MotionEvent.ACTION_DOWN:
          Log.i(TAG, "======onInterceptTouchEvent======ACTION_DOWN==");
        break;
        case MotionEvent.ACTION_MOVE:
          Log.i(TAG, "======onInterceptTouchEvent======ACTION_MOVE==");
        break;
        case MotionEvent.ACTION_UP:
          Log.i(TAG, "======onInterceptTouchEvent======ACTION_UP==");
        break;
      }
      return super.onInterceptTouchEvent(ev);}

    @Override
    public boolean onTouchEvent(MotionEvent event) {
      switch (event.getAction()){
        case MotionEvent.ACTION_DOWN:
          Log.i(TAG, "======onTouchEvent======ACTION_DOWN==");
        break;
        case MotionEvent.ACTION_MOVE:
          Log.i(TAG, "======onTouchEvent======ACTION_MOVE==");
        break;
        case MotionEvent.ACTION_UP:
          Log.i(TAG, "======onTouchEvent======ACTION_UP==");
        break;
      }
      return super.onTouchEvent(event);}

同样的新建类 CustomTextView 继承 TextView,重写 dispatchTouchEvent 和 onTouchEvent。同样重写 MainActivity 中的 dispatchTouchEvent 和 onTouchEvent。

布局文件

    <com.cuifei.test.CustomLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:tools="http://schemas.android.com/tools"
       android:layout_width="match_parent" android:layout_height="match_parent"
       >
     <com.cuifei.test.CustomButton
         android:layout_width="wrap_content" android:layout_height="wrap_content"
         android:layout_gravity="center" android:text="CustomButton" />

查看运行结果

事件分发 Log

事件分发Log

由图事件分发 Log 可以看出 ACTION_DOWN 传递的顺序示意图,如下图:

Android 事件分发示意图

Android 事件分发示意图(1)

在这之间我们并没有消费事件,由此可以看到事件从最外层逐级向内层传递,如果在这之间事件没有被消费掉,事件将由内层控件逐级向外层控件传递。

以 ACTION_DOWN 为例,详细分析 Android 事件传递的流程。 首先,MainActivity 捕获到 ACTION_DOWN 事件,并由 MainActivity 的 dispatchTouchEvent 方法分发给其子控件。因为 CustomLinearLayout 是 MainActivity 的子控件(我们暂且这么认为),所以 ACTION_DOWN 被分发给 CustomLinearLayout 并由 CustomLinearLayout 的 dispatchTouchEvent 继续分发。由于 CustomLinearLayout 是 ViewGroup 能够对事件进行拦截,所以 ACTION_DOWN 被分发给 CustomLinearLayout 的 onInterceptTouchEvent 方法。如果 onInterceptTouchEvent 返回 true 则拦截,否则不拦截。这里 CustomLinearLayout 的 onInterceptTouchEvent 返回 false,不对 ACTION_DOWN 拦截。所以 ACTION_DOWN 继续被分发给 CustomLinearLayout 的子控件 CustomTextView ,并由 CustomTextView 的 dispatchTouchEvent 进行分发。因为 CustomTextView 是 View 并且是最内层的 View ,所以 ACTION_DOWN 被分发给 CustomTextView 的 onTouchEvent 方法进行处理。本 demo 中 ACTION_DOWN 没有被 CustomTextView 的 onTouchEvent 处理掉,所以 ACTION_DOWN 向上传递给 CustomLinearLayout 的 onTouchEvent 。而 CustomLinearLayout 的 onTouchEvent 也没有处理 ACTION_DOWN,所以 ACTION_DOWN 继续向上传递给 MainActivity 的 onTouchEvent 。而 MainActivity 的 onTouchEvent 也没有处理,至此 ACTION_DOWN 将被释放,ACTION_DOWN 事件分发结束。

由图 事件分发 Log 我们还可以看出,ACTION_MOVE 和 ACTON_UP 并没有向内层控件传递,而是由 MainActivity 自己分发和处理。这说明 ACTION_MOVE 和 ACTON_UP 只传递到消费 ACTION_DOWN 的控件。为了验证这个我们将 CustomLinearLayout.onTouchEvent 的返回值改为 true 表示事件被 CustomLinearLayout 处理。

事件拦截Log

事件拦截Log

由 事件拦截Log可以看出 ACTION_MOVE 和 ACIONT_UP 只传递到了 CustomLinearLayout 这说明我们的上述观点是正确的。

由上面的代码可以看到和 onTouchEvent 方法一样,dispatchTouchEvent 和 onInterceptTouchEvent 方法也有一个 boolean 类型的返回值,这个返回值分别代表什么意思呢?

  • onInterceptTouchEvent 返回值为 true 时;表示事件被拦截,不在向内层控件传递。反之则向内层控件传递。
  • dispatchTouchEvent 返回 true 表示事件被 dispatchTouchEvent 自身处理消耗掉。至此,事件已经完结;返回 false 不再继续进行分发,并交由上层控件的onTouchEvent 方法进行处理;返回 super.dispatchTouchEvent(ev) 时,事件将继续传递。

最后两条结论的 log 将不再贴出,请读者自己验证。

最后给出本 demo 中事件分发的详细流程图,如下图:

Android 事件分发流程图

Android 事件分发流程图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值