当前位置: 首页 > 编程笔记 >

Android触摸事件传递机制

洪承天
2023-03-14
本文向大家介绍Android触摸事件传递机制,包括了Android触摸事件传递机制的使用技巧和注意事项,需要的朋友参考一下

前言:在Android开发中,经常会遇到触摸事件冲突,比如ViewPager的轮播图跟Fragment的划动事件冲突,或者轮播图跟下拉事件冲突,自定义view的事件处理等,本文章将会详细介绍Activity、View、ViewGroup三者的触摸事件传递机制,传递包括三个阶段:分发、拦截、消费。

本文章将会详细介绍Activity、View、ViewGroup三者的触摸事件传递机制,传递包括三个阶段:分发、拦截、消费。

一.触摸事件的类型

触摸事件对应的是 MotionEvent 类,事件类型主要有三种:

  1. ACTION_DOWN:用户按下操作,表示一次触摸事件的开始。
  2. ACTION_MOVE:在按下的情况下,进行移动。轻微的移动都会传递到该事件。
  3. ACTION_UP:用户手指离开屏幕,表示一次触摸事件的

注 :如果用户仅仅的是点击而已,则只会执行到 ACTION_DOWN 和 ACTION_UP 两个事件,不会执行到 ACTION_MOVE 事件。所以 ACTION_DOWN 和 ACTION_UP 是事件是必须的。

二.触摸事件的传递阶段

1.分发(Dispatch)

在Android系统中所有的触摸事件都是由 dispatchTouchEvent 方法进行分发的。该方法中判断事件是被消费( return true ),还是继续分发给子视图处理( return super.dispatchTouchEvent ),如果当前视图是ViewGroup或者其子类,则会调用 onInterceptTouchEvent 判断是否截拦。

@Override
 public boolean dispatchTouchEvent(MotionEvent event) {
  return super.dispatchTouchEvent(event);
 }

2.截拦(Intercept)

事件的截拦 InterceptTouchEvent 只存在于ViewGroup及其子类,activity和View是不存在该方法。该方法判断事件是被截拦 ( return true )并交给自身的 OnToucEvent 方法进行消费,还是继续传递给子视图( return super.InterceptTouchEvent 或者 return false )。

@Override
 public boolean onInterceptTouchEvent(MotionEvent ev) {
  return super.onInterceptTouchEvent(ev);
 }

3.消费(Consume)

事件的消费通过 OnTouchEvent 方法判断,是被消费( return true ),还是不处理( return false )并将事件传递给父视图的 OnTouchEvent 方法进行处理。

@Override
 public boolean onTouchEvent(MotionEvent event) {
  return super.onTouchEvent(event);
 }

所有拥有事件传递能力的类:

Activity: 拥有dispatchTouchEvent 、OnTouchEvent

ViewGroup: 拥有dispatchTouchEvent 、OnInterceptTouchEvent 、OnTouchEvent

View:拥有dispatchTouchEvent 、OnTouchEvent

三、View的事件传递机制

3.1 dome

虽然说ViewGroup是View的子类,但是这是说的View指的是除ViewGroup之外的View控件子类,首先定义一个MyTextView继承TextView,打印每次事件的触发以变了解事件传递的流程。

MyTextView 类

public class MyTextView extends TextView {
 private String tag = "MyTextView";
 public MyTextView(Context context) {
  super(context);
 }

 public MyTextView(Context context, AttributeSet attrs) {
  super(context, attrs);
 }

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


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

定义一个MainActivity来展现这个MyTextView,同时设置点击(onClick)和触摸(onTouch)监听。 MainActivity 类

public class MainActivity extends AppCompatActivity implements View.OnClickListener,View.OnTouchListener{
 private MyTextView mMyTextView;
 private String tag = "MainActiviy";

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  mMyTextView = findViewById(R.id.text_view);
  // 点击监听
  mMyTextView.setOnClickListener(this);
  // 触碰监听
  mMyTextView.setOnTouchListener(this);
 }


 // MyTextView 点击事件
 @Override
 public void onClick(View view) {
  switch (view.getId()){
   case R.id.text_view:
    Log.i(tag, "MyTextView onClick");
    break;
  }
 }

 // MyTextView 触碰事件
 @Override
 public boolean onTouch(View view, MotionEvent motionEvent) {
  switch (motionEvent.getAction()){
   case MotionEvent.ACTION_UP:
    Log.i(tag, "MyTextView onTouch ACTION_UP");
    break;
   case MotionEvent.ACTION_MOVE:
    Log.i(tag, "MyTextView onTouch ACTION_MOVE");
    break;
   case MotionEvent.ACTION_DOWN:
    Log.i(tag, "MyTextView onTouch ACTION_DOWN");
    break;
  }
  return false;
 }

 // Activity 的事件分发
 @Override
 public boolean dispatchTouchEvent(MotionEvent ev) {
  switch (ev.getAction()){
   case MotionEvent.ACTION_UP:
    Log.i(tag, "dispatchTouchEvent ACTION_UP");
    break;
   case MotionEvent.ACTION_MOVE:
    Log.i(tag, "dispatchTouchEvent ACTION_MOVE");
    break;
   case MotionEvent.ACTION_DOWN:
    Log.i(tag, "dispatchTouchEvent ACTION_DOWN");
    break;
  }
  return super.dispatchTouchEvent(ev);
 }

 // Activity 的事件消费
 @Override
 public boolean onTouchEvent(MotionEvent event) {
  switch (event.getAction()){
   case MotionEvent.ACTION_UP:
    Log.i(tag, "onTouchEvent ACTION_UP");
    break;
   case MotionEvent.ACTION_MOVE:
    Log.i(tag, "onTouchEvent ACTION_MOVE");
    break;
   case MotionEvent.ACTION_DOWN:
    Log.i(tag, "onTouchEvent ACTION_DOWN");
    break;
  }
  return super.onTouchEvent(event);
 }
}

3.2 打印日志

运行后,点击Text View反馈的打印日志

03-28 08:05:14.824 1219-1219/com.mvp.chenzhesheng.androidadvance I/MainActiviy: dispatchTouchEvent ACTION_DOWN
03-28 08:05:14.824 1219-1219/com.mvp.chenzhesheng.androidadvance I/MyTextView: dispatchTouchEvent ACTION_DOWN
03-28 08:05:14.824 1219-1219/com.mvp.chenzhesheng.androidadvance I/MainActiviy: MyTextView onTouch ACTION_DOWN
03-28 08:05:14.824 1219-1219/com.mvp.chenzhesheng.androidadvance I/MyTextView: onTouchEvent ACTION_DOWN
03-28 08:05:15.034 1219-1219/com.mvp.chenzhesheng.androidadvance I/MainActiviy: dispatchTouchEvent ACTION_UP
03-28 08:05:15.034 1219-1219/com.mvp.chenzhesheng.androidadvance I/MyTextView: dispatchTouchEvent ACTION_UP
03-28 08:05:15.034 1219-1219/com.mvp.chenzhesheng.androidadvance I/MainActiviy: MyTextView onTouch ACTION_UP
03-28 08:05:15.034 1219-1219/com.mvp.chenzhesheng.androidadvance I/MyTextView: onTouchEvent ACTION_UP
03-28 08:05:15.044 1219-1219/com.mvp.chenzhesheng.androidadvance I/MainActiviy: MyTextView onClick

dispatchTouchEvent 、 OnTouchEvent 这两个方法的返回值存在三种情况:

  1. 直接返回true。
  2. 直接返回false。
  3. 返回父类同名方法,super.dispatchTouchEvent 或者 super.OnTouchEvent。

由于拥有不同的返回值,所以事件传递流程也有不同,经过不断修改返回值测试,最终得到了点击事件的流程图,ACTION_DOWN 和 ACTION_UP 事件的传递流程是相同的。

3.3 事件传递流程图

从上面的流程图可以得出结论:

  1. 触摸事件是从 dispatchTouchEvent 开始的,默认返回父类同名方法 super ,事件将会依照嵌套层次从外向内传递( MainActivity 到 MyTextView ),到达最内层的 View 时,将由 View 的 OnTouchEvent 方法处理,该方法返回 true 时进行消费不再传递,返回 false 时再由内向外传递,由外层的 OnTouchEvent 处理。
  2. 如果外层向内层传递过程中,人为干扰返回 true 消费,则不会继续继续像内部传递。
  3. View 的事件控制顺序先执行 onTouch 再执行 onClick ,如果 onTouch 返回 true 消费,则不会继续传递,也不会执行 onClick 方法。

四、ViewGroup的事件传递机制

4.1 dome

ViewGroup是 View 的控件容器存在,拥有 dispatchTouchEvent 、 onInterceptTouchEvent 和 onTouchEvent 三个方法,比 View 多了一个 onInterceptTouchEvent 方法。为了更好的观察,我们需要自定义 MyRelativeLayout 继承 RelativeLayout 。

MyRelativeLayout类

public class MyRelativeLayout extends RelativeLayout {

 private final static String tag = "MyRelativeLayout";

 public MyRelativeLayout(Context context) {
  super(context);
 }

 public MyRelativeLayout(Context context, AttributeSet attrs) {
  super(context, attrs);
 }

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

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

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

main_activity.xml 文件

<?xml version="1.0" encoding="utf-8"?>
<com.mvp.chenzhesheng.androidadvance.MyRelativeLayout
 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.mvp.chenzhesheng.androidadvance.MyTextView
  android:id="@+id/text_view"
  android:clickable="true"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:text="Hello World!"

</com.mvp.chenzhesheng.androidadvance.MyRelativeLayout>

4.2 打印日志

04-02 08:47:57.980 1030-1030/com.mvp.chenzhesheng.androidadvance I/MainActiviy: dispatchTouchEvent ACTION_DOWN
04-02 08:47:58.000 1030-1030/com.mvp.chenzhesheng.androidadvance I/MyRelativeLayout: dispatchTouchEvent ACTION_DOWN
04-02 08:47:58.000 1030-1030/com.mvp.chenzhesheng.androidadvance I/MyRelativeLayout: onInterceptTouchEvent ACTION_DOWN
04-02 08:47:58.000 1030-1030/com.mvp.chenzhesheng.androidadvance I/MyTextView: dispatchTouchEvent ACTION_DOWN
04-02 08:47:58.010 1030-1030/com.mvp.chenzhesheng.androidadvance I/MainActiviy: MyTextView onTouch ACTION_DOWN
04-02 08:47:58.010 1030-1030/com.mvp.chenzhesheng.androidadvance I/MyTextView: onTouchEvent ACTION_DOWN
04-02 08:47:58.200 1030-1030/com.mvp.chenzhesheng.androidadvance I/MainActiviy: dispatchTouchEvent ACTION_UP
04-02 08:47:58.200 1030-1030/com.mvp.chenzhesheng.androidadvance I/MyRelativeLayout: dispatchTouchEvent ACTION_UP
04-02 08:47:58.200 1030-1030/com.mvp.chenzhesheng.androidadvance I/MyRelativeLayout: onInterceptTouchEvent ACTION_UP
04-02 08:47:58.200 1030-1030/com.mvp.chenzhesheng.androidadvance I/MyTextView: dispatchTouchEvent ACTION_UP
04-02 08:47:58.210 1030-1030/com.mvp.chenzhesheng.androidadvance I/MainActiviy: MyTextView onTouch ACTION_UP
04-02 08:47:58.210 1030-1030/com.mvp.chenzhesheng.androidadvance I/MyTextView: onTouchEvent ACTION_UP
04-02 08:47:58.260 1030-1030/com.mvp.chenzhesheng.androidadvance I/MainActiviy: MyTextView onClick

可以看到 MainActivity 和 MyTextView 的事件传递处理中添加了一层 MyRelativeLayout 。通过不同返回值测试,得到一套流程图。

4.3 流程图

从上面的流程图可以得出结论:

  1. 触摸事件传递是从 Activity 传递到 ViewGroup ,再传递到 View 。如果中间没有 ViewGroup 则直接从 Activity 传递到 View 。
  2. ViewGroup 通过 onInterceptTouchEvent 方法对事件进行截拦,如果返回 false 或者 super.onInterceptTouchEvent ,则事件会继续传递给子 View 。
  3. 子 View 中对事件进行消费后, ViewGroup 将不会接收到任何事件。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。

 类似资料:
  • 本文向大家介绍Android事件传递机制,包括了Android事件传递机制的使用技巧和注意事项,需要的朋友参考一下 实验环境 OS X 10.9 Eclipse(ADT) Android源码版本:API Level 19(Android 4.4) Android事件构成 在Android中,事件主要包括点按、长按、拖拽、滑动等,点按又包括单击和双击,另外还包括单指操作和多指操作。所有这些都构成了A

  • 触摸操作概述 浏览器的触摸 API 由三个部分组成。 Touch:一个触摸点 TouchList:多个触摸点的集合 TouchEvent:触摸引发的事件实例 Touch接口的实例对象用来表示触摸点(一根手指或者一根触摸笔),包括位置、大小、形状、压力、目标元素等属性。有时,触摸动作由多个触摸点(多根手指)组成,多个触摸点的集合由TouchList接口的实例对象表示。TouchEvent接口的实例对

  • 本文向大家介绍Android Touch事件传递机制相关面试题,主要包含被问及Android Touch事件传递机制时的应答技巧和注意事项,需要的朋友参考一下 在我们点击屏幕时,会有下列事件发生: Activity调用dispathTouchEvent()方法,把事件传递给Window; Window再将事件交给DecorView(DecorView是View的根布局); DecorView再传递

  • 触摸事件是手机游戏中最重要的事件,它易于创建,还能提供多种多样的功能。 让我们先了解一下什么是触摸事件,当你触摸移动设备的屏幕时,设备感受到被触摸,了解到被触摸的位置,同时取得触摸到的内容,然后你的触摸被回答。 这就是触摸事件。 如果你希望通过触摸控制屏幕下层的对象,那可以通过 优先级,达成这种需求,优先级高的对象能先处理事件。 创建触摸事件监听器: // Create a "one by on

  • 主要内容:jQuery Mobile 点击,实例,jQuery Mobile 点击不放(长按),实例,jQuery Mobile 滑动,实例,jQuery Mobile 向左滑动,实例,jQuery Mobile 向右滑动,实例触摸事件在用户触摸屏幕(页面)时触发。 触摸事件同样可应用与桌面电脑上:点击或者滑动鼠标! jQuery Mobile 点击 点击事件在用户点击元素时触发。 如下实例:当点击 <p> 元素时,隐藏当前的 <p> 元素: 实例 $("p").on("tap",functio

  • 我想知道是否有可能在GLSurfaceView上进行ImageView并获得两者各自的触摸事件。我的问题是,我想知道ImageView何时被按下(因此我将使用OnTouchListener)。但是,我仍然希望能够获得我的glsurfaceview的触碰事件。 提前感谢, 编辑:这是我的activity_main.xml