Android 安卓Touch事件的分发流程解析

简述

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

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

ViewGroup的相关事件有三个:

  • dispatchTouchEvent:分发事件。这个方法和事件分发机制密不可分,但这里不说这个。
  • onInterceptTouchEvent:拦截事件。继承此方法,返回true,表示拦截了此事件。
  • onTouchEvent:监听事件。继承此方法,处理事件,返回true表示消费了此事件。

View的相关事件只有两个:

  • dispatchTouchEvent
  • onTouchEvent

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

分发流程

分发流程用一个图来说明,看:

下面的描述只需要看一遍,理解了就行,然后上面这个图就会自动留在你的脑海中了,好简单的图。

  • 用户按下屏幕,滑动一下,再松开手,会发出一些列的事件:DOWN-MOVE-MOVE…-UP,这里主要说DOWN事件。
  • Activity首先会把DOWN事件分发给最底层的ViewGroup1的onInterceptTouchEvent方法。
  • onInterceptTouchEvent,继承并返回true,表示拦截此事件,不再传递下去;返回false,表示不拦截,事件直接传递给下一个ViewGroup的onInterceptTouchEvent方法。
  • onTouchEvent,继承并返回true,表示消费了此事件,不再传递下去;返回false,表示没有消费,继续传递下去给别的View处理。

注意:

如果一直没有View消费DOWN事件,则后面的MOVE、UP事件就不会再触发了。

比如上面的例子,如果ViewGroup1、ViewGrouip2和View的onTouchEvent都返回false的话,那么只会触发DOWN事件,后面的UP事件将不会收到了。

如果某个ViewGroup消费了DOWN事件,则后续的事件会把此ViewGroup当初View,也就是忽略它的onInterceptTouchEvent。

比如上面的例子,如果ViewGroup2的onTouchEvent返回true,则后面的UP事件会忽略ViewGroup2的onInterceptTouchEvent,会变成:ViewGroup1.onInterceptTouchEvent -> ViewGroup2.onTouchEvent。

实用的实践例子

首先,从一个简单示例入手:

先看一个示例如下图所示:

布局文件 :

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:id="@+id/container"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:layout_gravity="center"
  tools:context="com.example.touch_event.MainActivity"
  tools:ignore="MergeRootFrame" > 
  
  <Button
    android:id="@+id/my_button"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/hello_world" /> 
  
</FrameLayout> 

MainActivity文件:

public class MainActivity extends Activity { 
  
  @Override
  protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 
  
    Button mBtn = (Button) findViewById(R.id.my_button); 
    mBtn.setOnTouchListener(new OnTouchListener() { 
  
      @Override
      public boolean onTouch(View v, MotionEvent event) { 
        Log.d("", "### onTouch : " + event.getAction()); 
        return false; 
      } 
    }); 
    mBtn.setOnClickListener(new OnClickListener() { 
  
      @Override
      public void onClick(View v) { 
        Log.d("", "### onClick : " + v); 
      } 
    }); 
  
  } 
  
  @Override
  public boolean dispatchTouchEvent(MotionEvent ev) { 
    Log.d("", "### activity dispatchTouchEvent"); 
    return super.dispatchTouchEvent(ev); 
  } 
}

当用户点击按钮时会输出如下Log:

08-31 03:03:56.116: D/(1560): ### activity dispatchTouchEvent 
08-31 03:03:56.116: D/(1560): ### onTouch : 0 
08-31 03:03:56.196: D/(1560): ### activity dispatchTouchEvent 
08-31 03:03:56.196: D/(1560): ### onTouch : 1 
08-31 03:03:56.196: D/(1560): ### onClick : android.widget.Button{52860d98 VFED..C. ...PH... 0,0-1080,144 #7f05003d app:id/my_button} 

我们可以看到首先执行了Activity中的dispatchTouchEvent方法,然后执行了onTouch方法,然后再是dispatchTouchEvent --> onTouch, 最后才是执行按钮的点击事件。这里我们可能有个疑问,为什么dispatchTouchEvent和onTouch都执行了两次,而onClick才执行了一次 ? 为什么两次的Touch事件的action不一样,action 0 和 action 1到底代表了什么 ?

覆写过onTouchEvent的朋友知道,一般来说我们在该方法体内都会处理集中touch类型的事件,有ACTION_DOWN、ACTION_MOVE、ACTION_UP等,不过上面我们的例子中并没有移动,只是单纯的按下、抬起。因此,我们的触摸事件也只有按下、抬起,因此有2次touch事件,而action分别为0和1。我们看看MotionEvent中的一些变量定义吧:

public final class MotionEvent extends InputEvent implements Parcelable { 
// 代码省略 
  public static final int ACTION_DOWN       = 0;  // 按下事件 
  public static final int ACTION_UP        = 1;  // 抬起事件  
  public static final int ACTION_MOVE       = 2;  // 手势移动事件 
  public static final int ACTION_CANCEL      = 3;  // 取消 
 // 代码省略 
} 

可以看到,代表按下的事件为0,抬起事件为1,也证实了我们上面所说的。

在看另外两个场景:

1、我们点击按钮外的区域,输出Log如下 :

08-31 03:04:45.408: D/(1560): ### activity dispatchTouchEvent08-31  
03:04:45.512: D/(1560): ### activity dispatchTouchEvent 

2、我们在onTouch函数中返回true, 输出Log如下 :


                
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值