文档章节

Android事件传递机制

k
 kim366
发布于 2016/05/13 19:13
字数 2168
阅读 5
收藏 0
点赞 2
评论 0

      总结自其它人的博客,感觉比较清晰。


     参考:www.trinea.cn


Android Touch事件传递机制

介绍Android Touch事件的传递机制

不少朋友私信问到这个问题,那就推荐一篇我看到的对传递机制介绍最清楚的国外文章吧。本文略作翻译。

 

1、基础知识

(1) 所有Touch事件都被封装成了MotionEvent对象,包括Touch的位置、时间、历史记录以及第几个手指(多指触摸)等。

 

(2) 事件类型分为ACTION_DOWN, ACTION_UP, ACTION_MOVE, ACTION_POINTER_DOWN, ACTION_POINTER_UP, ACTION_CANCEL,每个事件都是以ACTION_DOWN开始ACTION_UP结束。

 

(3) 对事件的处理包括三类,分别为传递——dispatchTouchEvent()函数、拦截——onInterceptTouchEvent()函数、消费——onTouchEvent()函数和OnTouchListener

 

2、传递流程

(1) 事件从Activity.dispatchTouchEvent()开始传递,只要没有被停止或拦截,从最上层的View(ViewGroup)开始一直往下(子View)传递。子View可以通过onTouchEvent()对事件进行处理。

 

(2) 事件由父View(ViewGroup)传递给子View,ViewGroup可以通过onInterceptTouchEvent()对事件做拦截,停止其往下传递。

 

(3) 如果事件从上往下传递过程中一直没有被停止,且最底层子View没有消费事件,事件会反向往上传递,这时父View(ViewGroup)可以进行消费,如果还是没有被消费的话,最后会到Activity的onTouchEvent()函数。

 

(4) 如果View没有对ACTION_DOWN进行消费,之后的其他事件不会传递过来。

 

(5) OnTouchListener优先于onTouchEvent()对事件进行消费。

 

上面的消费即表示相应函数返回值为true。

 

更多请直接阅读PDF英文原文:Mastering the Android Touch System


自己对源码的分析:触摸屏事件的传递机制源码分析

          ViewGroup中onInterceptTouchEvent();
Implement this method to intercept all touch screen motion events. This allows you to watch events as they are dispatched to your children, and take ownership of the current gesture at any point.

Using this function takes some care, as it has a fairly complicated interaction with View.onTouchEvent(MotionEvent), and using it requires implementing that method as well as this one in the correct way. Events will be received in the following order:

     实现这个方法可以打断所有的触摸屏事件,并且在触摸屏事件被分发到子视图时,允许你监管这些事件,随时获取事件的所有权。
使用这个方法,需要考虑到很多方面, 因为这个方法和onTouchEvent()方法的交互比较复杂,使用这个方法 要求同样准确地实现onTouchEvent()方法。事件会以下面的顺序传递和接收:
1.  You will receive the down event here. 
     本方法收到按下事件 DOWN
2.  The down event will be handled either by a child of this view group, or given to your own onTouchEvent() method to handle; this means you should implement onTouchEvent() to return true, so you will continue to see the rest of the gesture (instead of looking for a parent view to handle it). Also, by returning true from onTouchEvent(), you will not receive any following events in onInterceptTouchEvent() and all touch processing must happen in onTouchEvent() like normal
       按下事件被当前视图的子视图处理,或被传递到当前viewgroup的onTouchEvent()方法中处理; 这就意味着,你需要实现onTouchEvent方法并返回true, 然后接着看剩下的其他手势(而不是寻找一个父视图处理), 其次, 通过在onTouchEvent中返回true后,你将不会再通过onInterceptTouchEvent()收到接下来其余的事件,并且所有的触屏事件处理都必须像通常情况一样在onTouchEvent中处理。
3.  For as long as you return false from this function, each following event (up to and including the final up) will be delivered first here and then to the target's onTouchEvent().
       只要你在本方法中返回false,那么接下来的所有事件都会再次被传递进来,然后寻找目标视图的onTouchEvent()方法。
4.  If you return true from here, you will not receive any following events: the target view will receive the same event but with the action  MotionEvent.ACTION_CANCEL , and all further events will be delivered to your onTouchEvent() method and no longer appear here.
     如果你通过本方法返回true, 那么你将收不到接下来的所有事件,目标视图将受到相同的事件,不过, 同时伴随着事件MotionEvent.ACTION_CANCEL,并且所有接下来的事件都将被传递到当前视图的onTouchEvent()中,再也不会出现在本方法中。
参数:
ev - The motion event being dispatched down the hierarchy.
返回:
Return true to steal motion events from the children and have them dispatched to this ViewGroup through onTouchEvent(). The current target will receive an ACTION_CANCEL event, and no further messages will be delivered here.
           public boolean onInterceptTouchEvent(MotionEvent ev) {
                return false;
          }


View中onTouchEvent()
/**
     * Implement this method to handle touch screen motion events.
     * <p>
     * If this method is used to detect click actions, it is recommended that
     * the actions be performed by implementing and calling
     * { @link #performClick()}. This will ensure consistent system behavior,
     * including:
     * <ul>
     * <li>obeying click sound preferences
     * <li>dispatching OnClickListener calls
     * <li>handling { @link AccessibilityNodeInfo#ACTION_CLICK ACTION_CLICK} when
     * accessibility features are enabled
     * </ul>
     *
     * @param event The motion event.
     * @return True if the event was handled, false otherwise.
     */
 public boolean onTouchEvent(MotionEvent event);

 Android的Touch事件处理机制比较复杂,特别是在考虑了多点触摸以及事件拦截之后。

      Android的Touch事件处理分3个层面:Activity层,ViewGroup层,View层。

      首先说一下Touch事件处理的几条基本规则。

      1.如果在某个层级没有处理ACTION_DOWN事件,那么该层就再也收不到后续的Touch事件了直到下一次ACTION_DOWN事件。

         说明:a.某个层级没有处理某个事件指的是它以及它的子View都没有处理该事件。

                 b.这条规则不适用于Activity层(它是顶层),它们可以收到每一个Touch事件。

                 c.如果没有处理ACTION_MOVE这类事件,不会有任何影响。

      2.如果ACTION_DOWN事件发生在某个View的范围之内,则后续的ACTION_MOVE,ACTION_UP和ACTION_CANCEL等事件都将被发往该View,即使事件已经出界了。

      3.第一根按下的手指触发ACTION_DOWN事件,之后按下的手指触发ACTION_POINTER_DOWN事件,中间起来的手指触发ACTION_POINTER_UP事件,最后起来的手指触发ACTION_UP事件(即使它不是触发ACTION_DOWN事件的那根手指)。

      4.pointer id可以用于跟踪手指,从按下的那个时刻起pointer id生效,直至起来的那一刻失效,这之间维持不变。

      5.如果一个ACTION_DOWN事件被父View拦截了,则任何子View不会再收到任何Touch事件了(这符合第1点的要求)。

      6.如果一个非ACTION_DOWN事件被父View拦截了,则那些上次处理了ACTION_DOWN事件的子View会收到一个ACTION_CANCEL事件,之后不会再收到任何Touch事件了,即使父View不再拦截后续的Touch事件。

      7.如果父View决定处理Touch事件或者子View没有处理Touch事件,则父View按照普通View的处理方式处理Touch事件,否则它根本不处理Touch事件(它只负责分发)。

      8.如果父View在onInterceptTouchEvent中拦截了事件,则onInterceptTouchEvent中不会再收到Touch事件了,事件被直接交给它自己处理(按照普通View的处理方式)。

      下面分层讲述一些细节。

      1.Activity层:


publicboolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) { //在这里交给View层处理returntrue;
        }
        return onTouchEvent(ev); // 如果View层没有处理,则在这里处理
}

      2.View层:


publicboolean dispatchTouchEvent(MotionEvent event) {
         // 省略了部分细节
         ListenerInfo li = mListenerInfo;
         if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
             returntrue;
         }
         if (onTouchEvent(event)) {
             returntrue;
         }
        returnfalse;
}

      View的onTouch方法代码比较多,主要的逻辑分两步:先是将事件交给TouchDelegate处理(如果有的话),如果TouchDelegate没有处理再自行处理;自行处理主要负责View状态的变换(如按下状态),长按事件,点击事件的检测与触发等。

      3.ViewGroup层(比较复杂):

      ViewGroup层处理Touch事件的总体逻辑是:先检测是否需要拦截,没有拦截的话下发给子View处理,如果子View没有处理再自行处理,自行处理的逻辑与View一样。

      拦截的逻辑是,将从down到up之间的所有事件看作一组事件,如果从down就拦截了,则组内的后续其它事件完全交给自己处理,不需要再进入拦截逻辑了;如果是从中间拦截,则先给子View发送cancel事件,组内的后续其它事件完全交给自己处理,不需要再进入拦截逻辑了。

      分发的逻辑是,在ACTION_DOWN事件的时候,寻找子View进行处理,称为寻找Target;如果没有找到Target,则自行处理;如果找到Target,则交由Target处理。

      从代码上看,dispatchTouchEvent负责分发逻辑,onTouchEvent负责真正的处理逻辑,一般应该重载onTouchEvent,只有特殊情况下才需要重载dispatchTouchEvent。




© 著作权归作者所有

共有 人打赏支持
k
粉丝 1
博文 129
码字总数 0
作品 0
朝阳
安卓自定义View进阶-事件分发机制原理

安卓自定义View进阶-事件分发机制原理 之前讲解了很多与View绘图相关的知识,你可以在 安卓自定义View教程目录 中查看到这些文章,如果你理解了这些文章,那么至少2D绘图部分不是难题了,大部...

猴亮屏 ⋅ 05/22 ⋅ 0

监听android:drawableLeft和android:drawableRight点击事件

监听android:drawableLeft和android:drawableRight点击事件 Android官方没有提供对android:drawableLeft和android:drawableRight点击事件的监听,但有些情况下,如下面的搜索栏, 需要在用户...

zhangphil ⋅ 04/17 ⋅ 0

10分钟了解Android的事件分发

什么是事件分发? 大家知道Android中的视图是由一个个View嵌套构成的层级视图,即一个View里包含有子View,而这个子View里面又可以再添加View。当用户触摸屏幕产生一系列事件时,事件会由高到...

codeGoogle ⋅ 05/21 ⋅ 0

理解 Android Hook 技术以及简单实战

什么是 Hook Hook 英文翻译过来就是「钩子」的意思,那我们在什么时候使用这个「钩子」呢?在 Android 操作系统中系统维护着自己的一套事件分发机制。应用程序,包括应用触发事件和后台逻辑处...

猴亮屏 ⋅ 04/17 ⋅ 0

Design Patterns in Android:责任链模式

前言 非常抱歉,本系列博客长达半年没更新了,今日偶得灵感,更新一波《设计模式Android篇:责任链模式》。点击此处查看《Design Patterns in Android》系列其他文章。 本文原创作者MichaelX。...

MichaelX ⋅ 05/14 ⋅ 0

Android开发权威指南(第2版)新书发布

《Android开发权威指南(第二版)》是畅销书《Android开发权威指南》的升级版,内容更新超过80%,是一本全面介绍Android应用开发的专著,拥有45章精彩内容供读者学习。  《Android开发权威指...

androidguy ⋅ 2013/09/05 ⋅ 0

Android6.0源码解读之Activity点击事件分发机制

本篇博文是Android点击事件分发机制系列博文的第四篇,主要是从解读Activity类的源码入手,根据源码理清Activity点击事件分发原理,并掌握Activity点击事件分法机制。特别声明的是,本源码解...

mynameishuangshuai ⋅ 2016/10/23 ⋅ 0

【Opengl Android】在安卓上渲染一个obj模型

工具:eclipse 一、获得一个obj文件并分析内容 3D溜溜网 点击打开链接 可以下载到很多模型文件 用3dmax打开 导出外部格式文件obj 即可得到一个obj模型 将格式改为txt 即可看到里面的数据内容...

qq_35263780 ⋅ 04/22 ⋅ 0

Android dispatchTouchEvent检测多点触摸事件是否落入在某一个View区域内

Android dispatchTouchEvent检测多点触摸事件是否落入在某一个View区域内 检测发生在Android设备屏幕上到的点击事件是否发生在某一个View的区域内,重点方法是touchEventInView,该方法进行具...

zhangphil ⋅ 04/24 ⋅ 0

Android性能优化:关于 内存泄露 的知识都在这里了!

前言 在中,内存泄露的现象十分常见;而内存泄露导致的后果会使得应用 本文 全面介绍了内存泄露的本质、原因 & 解决方案,最终提供一些常见的内存泄露分析工具,希望你们会喜欢。 目录 } Li...

Carson_Ho ⋅ 04/19 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

UI ,前端框架选型

Flat-UI

miaojiangmin ⋅ 4分钟前 ⋅ 0

Istio Service Mesh 教程

Istio Service Mesh 教程 作者 宋净超 | 5400字 | 阅读大约需要11分钟 | 归档于istio | 发表于 2018-05-22 标签 #Istio #教程,来自 https://servicemesher.github.io/blog/istio-service-m...

openthings ⋅ 9分钟前 ⋅ 0

scala swing

scala swing组件的库 https://github.com/scala/scala-swing scala swing的API文档 https://www.scala-lang.org/api/2.9.1/scala/swing/package.html...

whoisliang ⋅ 13分钟前 ⋅ 0

CentOS安装配置Nginx

安装依赖 yum install gcc yum install pcre-devel yum install zlib zlib-devel yum install openssl openssl-devel //一键安装上面四个依赖 yum -y install gcc zlib zlib-devel pcre-deve......

临江仙卜算子 ⋅ 20分钟前 ⋅ 0

开源 java CMS - FreeCMS2.8 依申请公开

项目地址:http://www.freeteam.cn/ 依申请公开 1. 转交申请公开 用户可以把申请公开转交给其他人办理,系统会记录此申请公开的转交记录。 注意:同时只能转交一个申请公开。 选择需要转交的...

freeteam ⋅ 24分钟前 ⋅ 0

以太坊 web3.py 签名转账

以太坊 web3.py 签名转账 本文节选自电子书《Netkiller Blockchain 手札》 Netkiller Blockchain 手札 Mr. Neo Chan, 陈景峯(BG7NYT) 中国广东省深圳市龙华新区民治街道溪山美地 518131 +86...

netkiller- ⋅ 29分钟前 ⋅ 0

年薪40W的程序员必会的技术有哪些?

很多人在问我,程序员如何拿高薪,如何做到年薪40W+,其实总结出来还是一句话,你的技术决定你的能力已经薪资。 那么什么样的技术人才才能拿到一份Java行业里面的高薪呢? 下面是我的一个总结...

码代码的小司机 ⋅ 30分钟前 ⋅ 0

jesque-spring使用及源码分析

1.使用 jesque结合spring使用,步骤如下: 1.1 在项目中添加maven依赖 <dependency> <groupId>net.lariverosc</groupId> <artifactId>jesque-spring</artifactId> <version>1.0.0</ve......

Funcy1122 ⋅ 31分钟前 ⋅ 0

OSChina 周二乱弹 —— 加班的代码不要枉费了我的童子功

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @小小编辑:推荐歌曲《29》- 未完成乐队 《29》- 未完成乐队 手机党少年们想听歌,请使劲儿戳(这里) @FalconChen :#看球提醒# 02:00 巴西v...

小小编辑 ⋅ 57分钟前 ⋅ 16

Docker Swarm的前世今生

概述 在我的《Docker Swarm集群初探》一文中,我们实际体验了Docker Swarm容器集群技术的魅力,与《Kubernetes实践录》一文中提到的Kubernetes集群技术相比,Docker Swarm没有Kubernetes显得...

CodeSheep ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部