深入理解 View 的事件传递机制

引言:现在 GitHub 上酷炫的 Android 控件越来越多,一方面我们可以让 App 各美观,另一方面我们这些开发者也可以从中学习到各种知识。写下这篇博文主要是记录研究自定义控件源码过程中接触到的知识盲区,帮助自己巩固知识的同时,也和大家交流学习,一起进步。

Demo源码

废话不多说,进入正题:

一、概述 View 事件传递机制

用户通过点击、滑动屏幕与 App 产生交互是移动互联网时代的交互基础,那么在 Android 中,用户的点击、滑动是怎么和 Android 系统产生交互的呢?

在 Android 中,我们所说的点击、滑动等事件,都被视为 MotionEvent ,而在 MotionEvent 中,我们的操作行为被归类为以下常量:

  • ACTION_DOWN
  • ACTION_UP
  • ACTION_MOVE
  • ACTION_POINTER_DOWN
  • ACTION_POINTER_UP
  • ACTION_CANCEL

除此以外,为了让系统更好地管理和操作这些事件,MotionEvent 还需要记录事件的发生时间,判断事件是单点触控/多点触控以及事件的发生时间。可能有人会问了,就这么点常量够我们判断我们的手势吗?莫慌,Google 对事件可是有着明确的区分标准呢:一次触控操作,起于 ACTION_DOWN 终于 ACTION_UP。简单的触控操作,如:点击、滑动等,很轻松就能通过这些常量判断出来;而复杂的手势,则需要根据你手指的滑动轨迹不断地对事件坐标进行分析了。

注:为了简化理解,后文中我将把所有和点击、滑动等等有关的事件归类为点击事件

二、View 事件的传递流程

之后的解析都会结合源码进行,没基础的小伙伴要认真跟上哦

在 Android 中,当一个点击事件被传入,首先执行 Actvity 的 dispatchTouchEvent() 方法接收事件,Activity 接收到事件之后交由根布局(即与 Activity 相关联的 Window 中的布局,一般是 ViewGroup,如:常见的 LinearLayout、RelativeLayout等)进行分发,若根布局不需要处理该事件,除非某一个布局在传递过程中通过 onInterceptTouchEvent() 方法将事件拦截,并“消费”该事件(消费的概念将在下面通过源码解释),否则事件将一直向下传递给子布局。若事件传递至最底层子布局中仍未被处理,则会反过来一直向上传递,此时每一级父布局都能处理该事件。若事件回传至根布局仍未被处理,则由 Activity 的 onTouchEvent() 方法终止事件。

注:OnTouchListener 处理事件的优先级高于 onTouchEvent()

这样一大段的叙述看下来,估计很多小伙伴都晕啦,其实俺也很晕哒~为了大家更好地理解,我先用一个 Demo 为大家介绍这个概念,再解析源码:

Demo 代码非常简单,就是自定义 Button 和 LinearLayout,在执行相关的方法时输出Log,Activity 里执行相关方法也输出 Log。点击流程如图:

上图很好地阐述了我们刚刚讲解的 View 事件传递流程,下面来看源码,更深一步地了解其中的机制:

首先,在 Activity 的 dispatchTouchEvent() 方法中对事件进行判断(两个判断语句不用管),然后进入 onTouchEvent() 方法

    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

在 onTouchEvent() 方法里面会有一个判断,shouldCloseOnTouch() 就是用于判断事件是否从尾部回传回来,true 代表事件应该不应该向下传递,而 false 代表事件未被消费,应该向下传递,交给子布局处理。

所以我们刚刚一直提到的“消费”的概念就是这个意思,事件在 View 链上传递一个来回,只要被处理了,并且处理它的 View 返回了 false,我们就认为事件被消费,如果返回 true ,我们则认为事件尚未被消费,仍需要向下传递,让对应的 View 处理它。

    public boolean onTouchEvent(MotionEvent event) {
        if (mWindow.shouldCloseOnTouch(this, event)) {
            finish();
            return true;
        }

        return false;
    }

现在 MyLinearLayout 通过 dispatchTouchEvent() 方法接收到事件,我们在 Demo 中只做了点击, dispatchTouchEvent() 方法有涉及 move 和 up,所以我只抽取和 down 有关的那部分来讲解

// Check for interception.
    final boolean intercepted;
    if (actionMasked == MotionEvent.ACTION_DOWN
            || mFirstTouchTarget != null) {
        final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
        if (!disallowIntercept) {
            intercepted = onInterceptTouchEvent(ev);
            ev.setAction(action); // restore action in case it was changed
        } else {
            intercepted = false;
        }
    } else {
        // There are no touch targets and this action is not an initial down
        // so this view group continues to intercept touches.
        intercepted = true;
    }

里面的逻辑很简单对吧?先判断要不要拦截,要拦截的话就把 intercepted 设为 true,使得后面的返回值也为 true ,进而让事件被当前 View 消费;不拦截的话,则将事件继续向下传递。

后面的 MyButton 同理。

网上看到几张图把整个流程展现地很好:

时间: 2024-10-05 17:25:33

深入理解 View 的事件传递机制的相关文章

Android View 触摸事件传递机制

PS:以现在的眼光看以前写的博客感觉写的很烂,或许或一段时间再看现在的博客会有同样的感觉.所以每时每刻都去学习,去发现和理解新的东西. 引言 由于之前写的一篇关于Android事件传递顺序的博客质量太差,可能是理解不到位的原因,故最近又花了许多时间再次去看Android源码,看完之后有了新的理解,所以打算重新整理这篇博客.理解Android触摸事件传递机制有助于日后的开发以及自定义一些手势效果等.注意:这篇博客是基于Android2.0源码来分析的,不管老版本还是新版本的Android,其内部触

理解Android View的事件传递机制

在android事件传递一般包括三个对象: Activity,ViewGroup,View,事件分发顺序为:Activity->ViewGroup->View,事件分发过程由 onTouchEvent() onInterceptTouchEvent() dispatchTouchEvent() 这三个方法协助完成, 其中View没有onInterceptTouchEvent()方法 原文地址:https://www.cnblogs.com/johnnyzhao/p/10350809.html

深入探索 ViewGroup 的事件传递机制

引言: ListView 中嵌套按钮,按钮点击事件无效,将按钮 setFocusable(false) 后才能正常使用点击事件的情况相信许多开发者都遇到过,很多人可能找到一个解决办法就把这个问题抛在了一边,但题主本着探索的精神从源码的角度找到了问题的答案 如果没有看过 深入理解 View 的事件传递机制 ,看今天的内容可能会有些吃力,所以建议大家还是先去看看我的这篇文章啦. 废话不多说,进入正题: 一.概述 ViewGroup 事件传递机制 研究 ViewGroup 之前,我们不妨先想一想:Vi

Android touch 事件传递机制

前言: (1)在自定义view的时候经常会遇到事件拦截处理,比如在侧滑菜单的时候,我们希望在侧滑菜单里面有listview控件,但是我们希望既能左右滑动又能上下滑动,这个时候就需要对触摸的touch事件进行拦截.这个时候我们就需要明白android touch 事件传递机制, (2)以前很多时候比较模糊,也许是网上看到也有很多事件传递的相关文章,但我看着头晕,解释不彻底,有的说得一半,总算不满足不满意,于是据我自己的理解来彻底的来整理下具体的是怎么个传递方式,分享给大家,希望大家看到有什么不对的

Android 开发艺术探究V第三章之view的事件分发机制

在介绍点击事件的传递机制,首先我们要分析的对象就是MOtionEvent,即点击事件,(当点击屏幕时由硬件传递过来,关于MotionEvent在View的基础知识中做了介绍),所谓的点击事件的分发就是MotionEvent的分发过程.即当一个MoTionEvent产生以后,系统需要把这个事件具体传递给一个具体的View,而这个传递过程就是分发过程,点击事件传递过程有三个很重要的方法,下面先来介绍这几个方法.  public boolean dispatchTouchEvent(MOtionEve

Android View触摸屏事件派发机制详解与源码分析

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober] 1.背景 最近在简书和微博还有Q群看见很多人说Android自定义控件(View/ViewGroup)如何学习?为啥那么难?其实答案很简单:"基础不牢,地动山摇." 不扯蛋了,进入正题.就算你不自定义控件,你也必须要了解Android控件的触摸屏事件传递机制(之所以说触摸屏是因为该

Android ViewGroup 触摸事件传递机制

引言 上一篇博客我们学习了Android View 触摸事件传递机制,不了解的同学可以查看Android View 触摸事件传递机制.今天继续学习Android触摸事件传递机制,这篇博客将和大家一起探讨ViewGroup的触摸事件传递机制. 示例 示例代码如下: public class MainActivity extends ActionBarActivity { private String TAG = "MainActivity"; private MyViewGroup pa

Android中事件传递机制的总结

事件传递虽然算不上某个单独的知识点,但是在实际项目开发中肯定会碰到,如果不明白其中的原理,那在设计各种滑动效果时就会感到很困惑. 关于事件的传递,我们可能会有以下疑问: 事件是如何传递的 事件是如何处理的 自定义view的时候,事件也冲突了怎么解决 带着这三个疑问,我们来总结一下事件传递机制是怎么回事. 一.事件分发的原理: 1.事件是如何传递的: (1)首先由Activity分发,分发给根View,也就是DecorView(DecorView为整个Window界面的最顶层View) (2)然后

Android View的事件分发机制

准备了一阵子,一直想写一篇事件分发的文章总结一下.这个知识点实在是太重要了. 一个应用的布局是丰富的,有TextView,ImageView,Button等.这些子View的外层还有ViewGroup.如RelativeLayout.LinearLayout.作为一个开发人员,我们会思考.当点击一个button,Android系统是如何确定我点的就是button而不是TextView的?然后还正确的响应了button的点击事件. 内部经过了一系列什么过程呢? 先铺垫一些知识能更加清晰的理解事件分