热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

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 文件

<&#63;xml version="1.0" encoding="utf-8"&#63;>


 

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 将不会接收到任何事件。

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


推荐阅读
  • Eclipse 中 Maven 的基础配置指南
    本文详细介绍了如何在 Eclipse 环境中配置 Maven,包括环境变量的设置、Maven 插件的安装与配置等关键步骤,旨在帮助开发者顺利搭建开发环境。 ... [详细]
  • ECharts 基础使用指南
    本文档提供了一个简单的 ECharts 使用示例,帮助初学者快速了解如何在网页中集成和使用 ECharts 创建图表。更多详细信息请参阅官方文档:https://www.echartsjs.com/zh/tutorial.html#5%20分钟上手%20ECharts ... [详细]
  • 本文介绍了ADB(Android Debug Bridge)的基本概念、安装方法、环境配置、连接真机步骤以及常用命令和高级技巧。ADB是一个强大的工具,适用于Android设备的开发和调试。 ... [详细]
  • 目录介绍01.CoordinatorLayout滑动抖动问题描述02.滑动抖动问题分析03.自定义AppBarLayout.Behavior说明04.CoordinatorLayo ... [详细]
  • 本文介绍了两种有效的方法来检查Android应用是否拥有特定权限,如媒体读写权限(media_rw)。通过这些方法,开发者和安全人员可以更好地了解应用的行为,确保其不会滥用权限。 ... [详细]
  • 本文详细介绍了ejabberd中的验证码服务、接收器以及服务器间通信的监督者和工作进程,包括它们的启动方式和主要功能。 ... [详细]
  • 本文探讨了如何在Android框架下通过自定义资源文件实现系统风格的统一,包括系统资源文件的位置、引用方法、系统主题的设置及修改等内容。 ... [详细]
  • 深入理解Django中的AJAX应用
    本文详细介绍了AJAX技术及其在Django框架中的应用。AJAX,即异步JavaScript和XML,允许网页在不重新加载整个页面的情况下与服务器交换数据并更新部分网页。 ... [详细]
  • Gradle基础概念与实践指南
    本文详细介绍了Gradle的基本概念、Groovy语言基础、Gradle的生命周期、项目管理以及任务配置等内容,旨在帮助开发者更好地理解和使用Gradle构建工具。 ... [详细]
  • 展望Kotlin未来发展:可能引入的新特性
    随着Kotlin社区的不断壮大,用户对于语言新特性的需求也在日益增长。本文基于YouTrack上的热门议题,探讨了Kotlin未来可能引入的一些新功能。 ... [详细]
  • 纵向|发生_ListView和EditText使用解决方案 ... [详细]
  • C# 对象转 JSON 字符串的方法与应用
    本文介绍如何在 C# 中使用一般处理程序(ASHX)将对象转换为 JSON 字符串,并通过设置响应类型为 application/json 来确保客户端能够正确解析返回的数据。同时,文章还提供了 HTML 页面中不依赖 jQuery 的 AJAX 方法来接收和处理这些 JSON 数据的具体实现。 ... [详细]
  • 利用Java与Tesseract-OCR实现数字识别
    本文深入探讨了如何利用Java语言结合Tesseract-OCR技术来实现图像中的数字识别功能,旨在为开发者提供详细的指导和实践案例。 ... [详细]
  • 作为一名计算机科学专业的大三学生,我在过去的一年里自学了Visual Basic (VB),但感觉进展缓慢。VB的学习是否仅仅局限于控件的使用?如何有效地学习API?此外,有人认为Basic语言已经过时,这对VB的未来意味着什么? ... [详细]
  • 本文详细介绍了如何通过修改 Jenkins 的配置文件来解决因权限设置不当导致的登录后页面为空的问题,包括多种权限配置策略的选择与应用。 ... [详细]
author-avatar
有点难
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有