揭秘 Android 事件分发机制:了解触摸事件的完整旅程 (揭秘angular)
在 Android 开发中,事件分发机制是一个对开发人员来说至关重要的概念。它决定了触摸事件如何在各种视图组件之间流动以及如何进行处理。了解 Android 事件分发的机制对于开发出响应且高效的应用程序至关重要。
事件分发的旅程
当用户触摸屏幕时,触摸事件将遵循以下旅程:
- 原始事件接收:事件被 WindowManager 接收,WindowManager 是管理 Android 窗口系统的组件。
- 触摸分发:WindowManager 将事件分发给应用程序 Activity 的根视图。
-
视图层次遍历:根视图对事件进行响应并决定是否对其进行处理。如果根视图不处理事件
Android 事件分发有两种主要类型:
- 吞噬事件:当视图处理事件并阻止其进一步分发的事件。触摸事件通常被吞噬,这意味着它们不会传播到更下层的视图。
- 冒泡事件:当事件从下层视图向上冒泡到其祖先视图的事件。按键事件通常会冒泡,这意味着它们会向上传递,直到被处理或达到根视图为止。
定制事件分发
开发人员可以通过覆写以下方法来定制 Android 事件分发:
- onTouchEvent:在 View 中覆写此方法以处理触摸事件。
- onInterceptTouchEvent:在 ViewGroup 中覆写此方法以控制子视图接收触摸事件的方式。
- dispatchTouchEvent:在 ViewGroup 中覆写此方法以定制触摸事件的分发方式。
最佳实践
处理 Android 事件分发时,请遵循以下最佳实践:
- 只在需要时处理事件,以避免不必要的处理。
- 使用吞噬事件来防止事件传播到不必要的方向。
- 使用冒泡事件来允许事件在视图层次结构中传播,直到被处理。
- 使用 ViewGroup 优化事件分发,例如 LinearLayout 和 RelativeLayout。
结论
Android 事件分发机制是应用程序响应用户输入的关键。通过了解其工作原理和最佳实践,开发人员可以创建出高效且响应迅速的应用程序。掌握事件分发对于任何有志于创建高质量 Android 应用程序的开发人员来说都是必不可少的技能。
请简述什么是android事件处理,并分析两种android事件处理机制的实现过程和区别
UI编程通常都会伴随事件处理,Android也不例外,它提供了两种方式的事件处理:基于回调的事件处理和基于监听器的事件处理。
对于基于监听器的事件处理而言,主要就是为Android界面组件绑定特定的事件监听器;对于基于回调的事件处理而言,主要做法是重写Android组件特定的回调函数,Android大部分界面组件都提供了事件响应的回调函数,我们主要重写它们就行。
一 基于监听器的事件处理
相比于基于回调的事件处理,这是更具“面向对象”性质的事件处理方式。在监听器模型中,主要涉及三类对象:
2)事件Event:事件封装了界面组件上发生的特定事件的具体信息,如果监听器需要获取界面组件上所发生事件的相关信息,一般通过事件Event对象来传递。
3)事件监听器Event Listener:负责监听事件源发生的事件,并对不同的事件做相应的处理。
基于监听器的事件处理机制是一种委派式Delegation的事件处理方式,事件源将整个事件委托给事件监听器,由监听器对事件进行响应处理。这种处理方式将事件源和事件监听器分离,有利于提供程序的可维护性。
举例:
View类中的OnLongClickListener监听器定义如下:(不需要传递事件)
[java] view plaincopyprint?
public interface OnLongClickListener {
boolean onLongClick(View v);
public interface OnLongClickListener {boolean onLongClick(View v);}
View类中的OnLongClickListener监听器定义如下:(需要传递事件MotionEvent)
[java] view plaincopyprint?
public interface OnTouchListener {
boolean onTouch(View v, MotionEvent event);
public interface OnTouchListener {boolean onTouch(View v, MotionEvent event);}
二 基于回调的事件处理
相比基于监听器的事件处理模型,基于回调的事件处理模型要简单些,该模型中,事件源和事件监听器是合一的,也就是说没有独立的事件监听器存在。当用户在GUI组件上触发某事件时,由该组件自身特定的函数负责处理该事件。通常通过重写Override组件类的事件处理函数实现事件的处理。
举例:
View类实现了接口中的一系列回调函数,因此,基于回调的事件处理机制通过自定义View来实现,自定义View时重写这些事件处理方法即可。
[java] view plaincopyprint?
public interface Callback {
// 几乎所有基于回调的事件处理函数都会返回一个boolean类型值,该返回值用于
// 标识该处理函数是否能完全处理该事件
// 返回true,表明该函数已完全处理该事件,该事件不会传播出去
// 返回false,表明该函数未完全处理该事件,该事件会传播出去
boolean onKeyDown(int keyCode, KeyEvent event);
boolean onKeyLongPress(int keyCode, KeyEvent event);
boolean onKeyUp(int keyCode, KeyEvent event);
boolean onKeyMultiple(int keyCode, int count, KeyEvent event);
public interface Callback { // 几乎所有基于回调的事件处理函数都会返回一个boolean类型值,该返回值用于 // 标识该处理函数是否能完全处理该事件// 返回true,表明该函数已完全处理该事件,该事件不会传播出去// 返回false,表明该函数未完全处理该事件,该事件会传播出去boolean onKeyDown(int keyCode, KeyEvent event);boolean onKeyLongPress(int keyCode, KeyEvent event);boolean onKeyUp(int keyCode, KeyEvent event);boolean onKeyMultiple(int keyCode, int count, KeyEvent event);}
三 比对
基于监听器的事件模型符合单一职责原则,事件源和事件监听器分开实现;
Android的事件处理机制保证基于监听器的事件处理会优先于基于回调的事件处理被触发;
某些特定情况下,基于回调的事件处理机制会更好的提高程序的内聚性。
四 基于自定义监听器的事件处理流程
在实际项目开发中,我们经常需要自定义监听器来实现自定义业务流程的处理,而且一般都不是基于GUI界面作为事件源的。这里以常见的app自动更新为例进行说明,在自动更新过程中,会存在两个状态:下载中和下载完成,而我们的程序需要在这两个状态做不同的事情,“下载中”需要在UI界面上实时显示软件包下载的进度,“下载完成”后,取消进度条的显示。这里进行一个模拟,重点在说明自定义监听器的事件处理流程。
4.1)定义事件监听器如下:
android事件分发机制 什么意思
android事件分发机制 就是一个触摸事件发生了,从一个窗口传递到一个视图,再传递到另外一个视图,最后被消费的过程,在android中还是比较复杂的传递流程如下:(1) 事件从()开始传递,只要没有被停止或拦截,从最上层的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。
Android Touch事件分发处理机制详解
Android应用的开发过程不可能不涉及到Touch事件的处理,简单地如设置OnClickListener、OnLongClickListener等监听器处理View的点击事件,复杂地如在自定义View中通过重写onTouchEvent来捕获用户交互事件以定制出各种效果,在使用的过程中或多或少会遇到一些奇怪的Bug,让你对Touch事件“从哪来,到哪去”产生迷之疑惑,经过多少次徘徊之后终于决定系统的分析下源码,本文就给大家分享下我的收获。
MotionEvent作为Touch事件的载体,采用时间片来管理Touch事件所有相关行为的数据,本文这样理解时间片这个概念:
通常MotionEvent会将触发当前事件的Pointer作为主要Pointer,其PointerIndex为0,而MotionEvent通过提供getX()这类不带index参数的接口以更方便的操作主要Pointer的数据。 了解了MotionEvent的组成结构之后,接下来就可以分析MotionEvent包含的事件类型了,MotionEvent通过getAction接口来获取事件Action,而Action中低8位地址存储的是事件类型(对于触摸事件来说,主要包括Down、Move、Up、Cancel、PointerDown、PointerUp),高8位地址存储的是PointerId(当事件类型为PointerDown、PointerUp时)。通常来说事件会以Down开始,以Up或Cancel结束,各事件所承担的角色以及各自的特点在分析事件分发与处理的过程时再详细说明。 另外,MotionEvent中的Flag需要说明一下:
本文仅分析Touch事件在Framework中Java层的传递,因此从事件传递到Activity开始分析。当Touch事件传递给Activity时,会调用(MotionEvent),Activity会将事件传递给其Window进行处理,实际会调用(MotionEvent),PhoneWindow会将该事件传递给Android中View层级中的顶层View(即DecorView)进行处理:
在Window未设置Callback的情况下,会调用父类的dispatchTouchEvent,DecorView继承自FrameLayout,然后FrameLayout并未实现dispatchEvent,因此最终调用,也就是Touch事件分发的核心逻辑所在,前文中提到MotionEvent中事件类型主要包括Down、Move、Up、Cancel、PointerDown、PointerUp,而dispatchTouchEvent根据事件的不同类型会做不同处理,因此这里分别进行分析:
Down事件处理
非异常情况下,Touch事件的事件周期总是以Down事件开始的,因此Down事件在整个事件分发逻辑中起关键作用,将决定了后续Move、Up及Cancel事件的处理主体,先看一张Down事件分发的流程图:
从流程图中可以看到,Down事件的分发逻辑主要目的在于寻找到能处理该Touch事件的View控件(该View为以当前ViewGroup为Root节点的View层级中的View,利用寻找到的View创建事件处理Target),整个处理逻辑主要包含以下几步:
Move、Up、Cancel事件处理
完成Down事件的分发逻辑后,就确定了该Down事件后续Move、Up及Cancel事件的处理主体(注意:这里并没有确定PointerDown事件的处理主体,关于PointerDown事件的分发逻辑稍后分析),先通过一张流程图来感受下Move、Up、Cancel事件的分发逻辑:
从流程图可以看出,对于Move、Up、Cancel事件的分发步骤如下:
PointerDown事件处理
PointerDown事件是在支持多Pointer(调用setMotionEventSplittingEnabled将FLAG_SPLIT_MOTION_EVENTS置位)的环境下,当有新的Pointer按下时产生的,该事件处理的特殊性在于会重新遍历View层级,寻找可以处理新Pointer事件的Target,具体流程参考Down事件的分发逻辑;遍历结束若仍没有找到处理该事件的Target,则会将新Pointer的处理权设置给已有Target中最早被添加的Target。完成Target的寻找之后,会将该事件通过dispatchTransformedTouchEvent传递至所有已有Target进行处理,可以通过下面流程图,对PointerDown事件的处理有一个更全局的认识:
PointerUp事件处理
相对于Up事件来说,对于PointerUp事件的处理区别在于当传递至所有已有Target结束之后并不能标记以Down事件起始的整个事件周期结束,仅能标记其关联Pointer(以PointerDown事件起始)的事件周期结束,因此不会清除所有状态,而仅会从已有Target中移除掉与该Pointer相关的部分。
onInterceptTouchEvent
在ViewGroup进行事件分发的过程中,会调用该函数来确定是否需要拦截事件,当该函数返回true时该事件将会被拦截,即不会进行正常的View层级传递,而是直接由该ViewGroup来处理,而拦截后的操作需要根据拦截事件的类型不同而不同:
dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits)
在将事件传递给Target进行处理之前会调用该函数对MotionEvent进行处理:
(int idBits)
判断一个View控件是否消费一个事件,是由的返回值来决定的,而用于寻找事件的最终消费者,话不多说,还是通过一张流程图来个直观感受:
从流程图中可以看出,View会根据ouch事件对Scroll状态进行调整,并寻找该事件的最终处理器:
将向其直接ViewGroup返回是否消费掉该事件,返回值将决定上级ViewGroup是否需要继续询问其他子View是否需要消费该事件。这就是View中分发事件的逻辑,真是简单粗暴!
从的分析中可以发现当未对View设置mTouchListener或mTouchListener未消费掉该事件时,Touch事件最终将由来决定是否消费,自定义View可以重写该方法实现自身的逻辑,此处仅分析View中的通用处理逻辑:
从上述分析可以很开心地发现熟悉的onClick及onLongClick事件的产生逻辑,若是之前没看过类似的文章,应该会有原来如此的感觉吧,哈哈~~
至此,Touch事件的分发与处理流程算是走通了,个人看完整个源码之后有种豁然开朗的感觉,能很清晰的分析向“为什么事件有时候传到某个View有时候却不传?”、“有时候只传前面几个事件后面却不传了?”等问题,也希望本文的分析能让你更清晰地感知Android中Touch事件的传递流程,如果发现文中有何错误,希望不吝赐教!
若对本页面资源感兴趣,请点击下方或右方图片,注册登录后
搜索本页相关的【资源名】【软件名】【功能词】或有关的关键词,即可找到您想要的资源
如有其他疑问,请咨询右下角【在线客服】,谢谢支持!
相关文章
- 让您的图像说出话来:探索在线图片制作平台的惊人力量 (让您的图像说出来英语)
- 告别平庸:掌握在线图片制作的艺术,打造惊人的视觉效果 (告别平庸的自己)
- 在数字化时代释放您的创造力:在线制作引人入胜的图片 (在数字化时代企业数字化转型的大方向是什么)
- 让您的内容焕发活力:体验我们无与伦比的动态海报生成器,点亮您的社交媒体形象 (让您的内容焕发出来)
- 释放您的想象力:用我们先进的动态海报生成器打造引人注目的视觉效果 (释放您的想象英文)
- 从人群中脱颖而出:用我们创新的动态海报生成器为您的广告增加活力 (从人群中脱颖而出英文)
- 拥抱视觉动态:探索我们革命性的动态海报生成器,让您的内容栩栩如生 (拥抱视觉动态表情包)
- 解锁无限创意:利用我们的直观动态海报生成器为您的品牌赋能 (无限创意屋)
- 提升您的视觉传播:发现我们前沿的动态海报生成器如何改变游戏规则 (提升您的视觉能力)
- 告别静态内容!用我们的革命性的动态海报生成器让您的内容脱颖而出 (告别静态内容怎么写)
发表评论
评论列表
- 这篇文章还没有收到评论,赶紧来抢沙发吧~