【易客吧】_全网激活码总代_激活码商城

您现在的位置是:首页 > 热门资讯 > 正文

热门资讯

Android 事件分发机制与多点触控:处理多个同时发生的触摸的挑战 (android studio)

用户投稿2024-04-19热门资讯17

简介

Android 提供了一个健壮且灵活的事件分发机制,允许应用程序处理用户输入,包括触摸、键盘和手势。多点触控功能使应用程序能够响应用户同时使用多个手指进行交互,增加了交互的灵活性。

事件分发机制

Android 事件分发机制基于以下步骤:1. 事件源: 用户操作或系统事件(如传感器数据)触发事件,称为动作(action)。2. 事件传递: 事件通过一系列窗口(ViewGroup 和 View)传递,称为视图层次结构。3. 事件侦听器: 每个视图都包含事件侦听器,用于处理特定类型的动作。4. 事件分发: 事件沿着视图层次结构向下传递,直到找到可以处理事件的视图。5. 事件消费: 视图可以消费事件,表示它已处理事件并阻止其进一步传递。

多点触控

多点触控功能允许应用程序同时跟踪多个手指输入。Android 支持以下多点触控动作:DOWN: 手指按压在屏幕上。MOVE: 手指移动。UP: 手指从屏幕上抬起。POINTER_DOWN: 另一个手指按压在屏幕上。POINTER_UP: 另一个手指从屏幕上抬起。

处理多点触控事件

为了处理多点触控事件,应用程序需要:1. 检测多点触控支持: 检查 `android.os.Build.VERSION.SDK_INT` 以确定设备是否支持多点触控。2. 注册多点触控侦听器: 为视图注册 `OnMultiTouchListener` 侦听器以捕获多点触控事件。3. 处理多点触控事件: 在 `onMultiTouch` 方法中,应用程序可以访问事件对象并提取信息,如手指ID、位置和动作类型。以下是一个示例代码,展示了如何处理多点触控事件:```javapublic class MyView extends View implements OnMultiTouchListener {private List fingers = new ArrayList<>();public MyView(Context context) {super(context);setOnMultiTouchListener(this);}@Overridepublic boolean onMultiTouch(View v, MotionEvent event) {int action = event.getActionMasked();int pointerIndex = event.getActionIndex();switch (action) {case MotionEvent.ACTION_DOWN:case MotionEvent.ACTION_POINTER_DOWN:FingerEvent newFinger = new FingerEvent(event.getPointerId(pointerIndex), event.getX(pointerIndex), event.getY(pointerIndex));fingers.add(newFinger);break;case MotionEvent.ACTION_UP:case MotionEvent.ACTION_POINTER_UP:iterators.removeIf(finger -> finger.id == event.getPointerId(pointerIndex));break;case MotionEvent.ACTION_MOVE:for (int i = 0; i < event.getPointerCount(); i++) {FingerEvent finger = findFingerById(event.getPointerId(i));finger.x = event.getX(i);finger.y = event.getY(i);}break;}invalidate();return true;}private FingerEvent findFingerById(int id) {for (FingerEvent finger : fingers) {if (finger.id == id) {return finger;}}return null;}}```

最佳实践

在处理多点触控事件时,遵循以下最佳实践非常重要:减少计算: 避免在处理事件时进行繁重的计算,因为这可能会影响性能。使用手指ID: 使用手指ID跟踪特定手指,而不是依赖手指索引。处理所有手指操作: 确保应用程序可以处理所有潜在的多点触控操作,包括手指按下、移动和抬起。使用MotionEvent对象: `MotionEvent` 对象提供有关触摸事件的详细信息,例如触点数量、位置和动作类型。

结论

Android 事件分发机制与多点触控功能提供了强大的工具,用于在 Android 应用程序中处理用户输入。通过理解这些机制,开发人员可以创建能够响应用户交互并在多点触控设备上提供出色体验的应用程序。

Framework事件机制——手撕Android事件处理的三种方法

Android的事件处理的三种方法:

Android 事件分发机制与多点触控:处理多个同时发生的触摸的挑战 (android studio) 第1张

setOnClickListener,setOnLongClickListener、setOnTouchListener

注意:如果onTouchEvent方法return true,则单击事件和长摁事件不再执行;若onLongClick方法返回true,则单击事件不再处理。

需要定义继承组件的类,重写回调方法Touch方法执行时,先被Activity捕获,DispatchTouchEvent方法处理。return false,交给上层的onTouchEvent方法处理;return (ev),则传递给最外层的View。

View用Dispatch方法处理,return false,由上层的onTouchEvent方法处理。如果返回(ev),则本层的onInterceptTouchEvent拦截,如果拦截true,则拦截,false不拦截,传递给子View的DispatchTouchEvent处理。

常用的回调方法:onKeyDown,onKeyLongPress,onKeyUp,onTouchEvent,onTrackballEvent(轨迹球事件)监听和回调同时存在时,先调用监听。

流程模型图:

Event source 事件源 Event 事件 Event Listener 事件监听器 下面我们来看一下点击事件和触摸事件的监听三要素具体是那部分:

由于点击事件比较简单,系统已经帮我们处理了,并没有找到具体事件是哪个。

单击事件监听器必须实现的接⼝ 创建上下⽂菜单事件 焦点改变事件 按键事件监听器 长按事件监听器 触摸屏事件监听器

⾸先,事件监听机制中由事件源,事件,事件监听器三类对象组成。 事件监听器处理流程:

在此以OnClickListener单击事件为例使用intent来实现页面的跳转

监听事件处理是事件源与事件监听器分开的而基于回调的事件处理UI组件不但是事件源,而且还是事件监听器,通过组件的相关回调方法处理对应的事件。

Ⅰ. 自定义View类,继承自需要的View UI类。ex :自定义 MyButton按钮类 extends 基础Button类

Ⅱ. 复写回调函数。ex:public boolean onTouchEvent(MotionEvent event)

每一个事件回调方法都会返回一个boolean值,①.如果返回true:表示该事件已被处理,不再继续向外扩散,②.如果返回false:表示事件继续向外扩散

而说到基于回调就离不开监听机制 。

几乎所有基于回调的事件处理方法都有一个boolean类型的返回值,该返回值用于表示该处理方法是否能完全处理该事件。 如果处理事件的回调方法返回true,表明该处理方法已经完全处理改事件,该事件不会传播出去。 如果处理事件的回调方法返回false,表明该处理方法并未完全处理该事件,该事件会传播出去。 对于基于回调的时间传播而言,某组件上所发生的事件不仅会激发该组件上的回调方法,也会触发该组件所在Activity的回调方法——只要事件能传播到该Activity。

这里是在模拟器里进行的测试,这里按下键盘(而不是点击),会看到 logcat 中的输出,如下:

View类实现了接口中的一系列回调函数,因此,基于回调的事件处理机制通过自定义View来实现,自定义View时重写这些事件处理方法即可。

Handler是一个消息分发对象。

Handler是Android系统提供的一套用来更新UI的机制,也是一套消息处理机制,可以通过Handler发消息,也可以通过Handler处理消息。

在下面介绍Handler机制前,首先得了解以下几个概念:

在子线程执行完耗时操作,当Handler发送消息时,将会调用 ,向消息队列中添加消息。 当通过 开启循环后,会不断地从消息池中读取消息,即调用 , 然后调用目标Handler(即发送该消息的Handler)的dispatchMessage 方法传递消息, 然后返回到Handler所在线程,目标Handler收到消息,调用handleMessage 方法,接收消息,处理消息。

从上面可以看出,在子线程中创建Handler之前,要调用() 方法,Handler创建后,还要调用() 方法。而前面我们在主线程创建Handler却不要这两个步骤,因为系统帮我们做了。

初始化Looper :

从上可以看出,不能重复创建Looper,每个线程只能创建一个。创建Looper,并保存在ThreadLocal 。其中ThreadLocal是线程本地存储区(Thread Local Storage,简称TLS),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域。

开启Looper

发送消息 :

post方法:

send方法:

在子线程中,进行耗时操作,执行完操作后,发送消息,通知主线程更新UI。

本文讲解了三个方面;Android事件机制;基于监听、基于回调以及Handler消息处理。还有许多没有讲解到的知识点,我总结在了整理的一套Android进阶笔记里面;需要学习进阶的同学可以前往获取: Frame Work源码解析手册 、 Android核心技术进阶手册、实战笔记、面试题纲资料

MotionEvent详解

Android 将所有的输入事件都放在了 MotionEvent 中,随着安卓的不断发展壮大,MotionEvent 也开始变得越来越复杂,下面是我自己整理的 MotionEvent 大事记:

以上仅仅是简要的说明几次比较大的变动,细小的修复和更新不计其数,此处就不一一列出了,反正也没人关心这些东西。 MotionEvent 负责集中处理所有类型设备的输入事件,但是由于某些设备使用的几率较小本文会忽略讲解,或者简要讲解,例如: 1、轨迹球只出现在最早的设备上,现代的设备上已经见不到了,本文不再叙述。 2、触控笔和手指处理流程基本相同,不再多说。 3、鼠标在手机上使用概率也比较小,会在文末简要介绍。

和以下的几个方法:

单点触控一次简单的交互流程是这样的:

手指落下(ACTION_DOWN) -> 多次移动(ACTION_MOVE) -> 离开(ACTION_UP)

针对单点触控的事件处理一般是这样写的:

相信小伙伴对此已经非常熟悉了,经常使用的东西,我也不啰嗦了。

但其中有两个比较特殊的事件:ACTION_CANCEL和ACTION_OUTSIDE。 为什么说特殊呢,因为它们是由程序触发而产生的,而且触发条件也非常特殊,通常情况下即便不处理这两个事件也没有什么问题。接下来我们就扒一扒它们的真面目:

ACTION_CANCEL的触发条件是事件被上层拦截 ,然而我们在事件分发机制原理一文中了解到当事件被上层 View 拦截的时候,ChildView 是收不到任何事件的,ChildView 收不到任何事件,自然也不会收到ACTION_CANCEL了,所以说这个ACTION_CANCEL的正确触发条件并不是这样,那么是什么呢?

事实上,只有上层 View 回收事件处理权的时候,ChildView 才会收到一个ACTION_CANCEL事件。

这样说可能不太容易理解,咱举个例子?

ACTION_OUTSIDE 的触发条件更加奇葩,从字面上看,outside 意思不就是超出区域么?然而不论你如何滑动超出控件区域都不会触发ACTION_OUTSIDE这个事件。相信很多魔法师都对此很是疑惑,说好的超出区域呢?

实际上这个事件根本就不是在这里用的,看官方解释(装一下逼):

我们知道,正常情况下,如果初始点击位置在该视图区域之外,该视图根本不可能会收到事件,然而,万事万物都不是绝对的,肯定还有一些特殊情况,你可曾还记得点击 Dialog 区域外关闭吗?Dialog 就是一个特殊的视图(没有占满屏幕大小的窗口),能够接收到视图区域外的事件(虽然在通常情况下你根本用不到这个事件),除了 Dialog 之外,你最可能看到这个事件的场景是悬浮窗,当然啦,想要接收到视图之外的事件需要一些特殊的设置。

由于这个事件用到的几率比较小,此处就不展开叙述了,以后用到的时候再详细讲解。

Android 在 2.0 版本的时候开始支持多点触控,一旦出现了多点触控,很多东西就突然之间变得麻烦起来了,首先要解决的问题就是 多个手指同时按在屏幕上,会产生很多的事件,这些事件该如何区分呢?

为了区分这些事件,工程师们用了一个很简单的办法-- 编号,当手指第一次按下时产生一个唯一的号码,手指抬起或者事件被拦截就回收编号,就这么简单。

第一次按下的手指特殊处理作为主指针,之后按下的手指作为辅助指针 ,然后随之衍生出来了以下事件(注意增加的事件和事件简介的变化):

和以下方法:

由于多点触控部分涉及内容比较多,也很复杂,我准备单独用一篇文章进行详细叙述,所以这里只叙述一些基础的内容作为铺垫:

当多个手指在屏幕上按下的时候,会产生大量的事件,如何在获取事件类型的同时区分这些事件就是一个大问题了。

一般来说我们可以通过为事件添加一个int类型的index属性来区分,但是我们知道谷歌工程师是有洁癖的(在自定义View分类与流程的onMeasure中已经见识过了),为了添加一个通常数值不会超过10的index属性就浪费一个int大小的空间简直是不能忍受的,于是工程师们将这个index属性和事件类型直接合并了。

int类型共32位(0x),他们用最低8位(0x ff )表示事件类型,再往前的8位(0x0000 ff 00)表示事件编号,以手指按下为例讲解数值是如何合成的:

注意: 上面表格中用粗体标示出的数值,可以看到随着按下手指数量的增加,这个数值也是一直变化的,进而导致我们使用getAction()获取到的数值无法与标准的事件类型进行对比,为了解决这个问题,他们创建了一个getActionMasked()方法,这个方法可以清除index数值,让其变成一个标准的事件类型。 1、多点触控时必须使用getActionMasked()来获取事件类型。 2、单点触控时由于事件数值不变,使用getAction()和getActionMasked()两个方法都可以。 3、使用 getActionIndex() 可以获取到这个index数值。不过请注意,getActionIndex() 只在 down 和 up 时有效,move 时是无效的。

目前来说获取事件类型使用getActionMasked()就行了,但是如果一定要编译时兼容古董版本的话,可以考虑使用这样的写法:

虽然前面刚刚说了一个 actionIndex,可以使用 getActionIndex() 获得,但通过 actionIndex 字面意思知道,这个只表示事件的序号,而且根据其说明文档解释,这个 ActionIndex 只有在手指按下(down)和抬起(up)时是有用的,在移动(move)时是没有用的,事件追踪非常重要的一环就是移动(move),然而它却没卵用,这也太不实在了 ( ̄Д ̄)ノ

PointId 在手指按下时产生,手指抬起或者事件被取消后消失,是一个事件流程中唯一不变的标识,可以在手指按下时 通过getPointerId(int pointerIndex)获得。 (参数中的 pointerIndex 就是 actionIndex)

关于事件流的追踪等问题在讲解多点触控时再详细讲解。

由于我们的设备非常灵敏,手指稍微移动一下就会产生一个移动事件,所以移动事件会产生的特别频繁,为了提高效率,系统会将近期的多个移动事件(move)按照事件发生的顺序进行排序打包放在同一个 MotionEvent 中,与之对应的产生了以下方法:

注意:

下面是官方文档给出的一个简单使用示例:

获取事件发生的时间。

MotionEvent支持获取某些输入设备(手指或触控笔)的与屏幕的接触面积和压力大小,主要有以下方法:

注意:

1、获取接触面积大小和获取压力大小是需要硬件支持的。 2、非常不幸的是大部分设备所使用的电容屏不支持压力检测,但能够大致检测出接触面积。 3、大部分设备的getPressure()是使用接触面积来模拟的。 4、由于某些未知的原因(可能系统版本和硬件问题),某些设备不支持该方法。

我用不同的设备对这两个方法进行了测试,然而不同设备测试出来的结果不相同,之后经过我多方查证,发现是系统问题,有的设备上只有getSize()能用,有的设备上只有getPressure()能用,而有的则两个都不能用。

由于获取接触面积和获取压力大小受系统和硬件影响,使用的时候一定要进行数据检测,以防因为设备问题而导致程序出错。

由于触控笔事件和手指事件处理流程大致相同,所以就不讲解了,这里讲解一下与鼠标相关的几个事件:

注意:

1、这些事件类型是 安卓4.0 (API 14) 才添加的。 2、使用getActionMasked()获得这些事件类型。 3、这些事件不会传递到onTouchEvent(MotionEvent)而是传递到onGenericMotionEvent(MotionEvent)。

输入设备类型判断也是安卓4.0 (API 14) 才添加的,主要包括以下几种设备:

使用getToolType(int pointerIndex)来获取对应的输入设备类型,pointIndex可以为0,但必须小于getPointerCount() 。

文章搬运自安卓自定义View进阶-MotionEvent详解

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 事件分发机制与多点触控:处理多个同时发生的触摸的挑战 (android studio) 第2张

发表评论

评论列表

  • 这篇文章还没有收到评论,赶紧来抢沙发吧~
欢迎你第一次访问网站!