找回密码
 会员注册
查看: 14|回复: 0

抖音Android无障碍开发知识总结

[复制链接]

5

主题

0

回帖

16

积分

新手上路

积分
16
发表于 2024-10-1 11:36:07 | 显示全部楼层 |阅读模式
抖音无障碍背景国家近期开展了无障碍建设活动。为了积极响应国家号召,为抖音视障用户能够得到更好的交互体验,对抖音无障碍功能进行了专项治理和改造。无障碍模式下的使用方法抖音的无障碍功能实现主要是通过开启 Google TalkBack(或第三方屏幕阅读)功能,将用户在屏幕上触摸选中区域的内容朗读出来,使得视障人士可以根据朗读的内容获取自己当前操作区域的信息,从而提升视障人士的使用和交互体验。常用的操作手势:浏览某个 View:单击点击某个 View:双击沿某个方向滑动:双指沿所需方向滑动顺序浏览页面:单指左右滑动本文的目的使研发同学对无障碍功能有一个更加全面的认识和了解,方便研发同学进行无障碍功能的开发。本文将分为无障碍功能实现原理和无障碍功能实现实例两部分进行介绍。无障碍功能实现原理系统结构无障碍功能的实现需要以下三个部分的支持:辅助 App(例如 TalkBack)、被辅助 app(用户使用的 app,例如抖音头条等)以及系统服务 AccessibilityManagerService,这三者之间的关系如下图所示:从上图中可以看出,以上的流程主要涉及到三个进程的通信。辅助 app 和被辅助 app 不需要直接跟被辅助的 app 通信,而是通过 SystemServer 进行中转通信,这个过程主要涉及到了四个 aidl 接口:被辅助 app->SystemServer(IAccessibilityManager.aidl)当被辅助 app 产生触摸事件后,会通过该接口发送无障碍事件给 SystemServer 进程的 AccessibilityManagerService。SystemServer->辅助 app(IAccessibilityServiceClient.aidl)当 SystemServer 接收到被辅助 app 发送的无障碍事件时,会将事件通过该接口传递给辅助 app(例如 TalkBack)进行处理。辅助 app->SystemServer(IAccessibilityServiceConnection.aidl)SystemServer->被辅助 app(IAccessibilityInteractionConnection.aidl)当需要被辅助 app 的某个 View 的信息时,可以通过这两个接口的 findAccessibilityNodeInfosByViewId 方法实现。无障碍事件传递流程当用户触摸屏幕时,会经过以下的流程将触摸事件传递给被触摸的 View:下面本文将主要分析以上流程中四个重点部分的内容:无障碍模式下的事件转换、触摸事件到 Activity 的传递过程、事件传递给具体的 View 的分发过程以及最终无障碍事件的执行流程。1.无障碍模式下的事件转换在 TalkBack 开启的状态下,由于 TalkBack 的无障碍服务中声明了 android:canRequestTouchExplorationMode=''true'' ,因此开启 TalkBack 后 AccessibilityManagerService 会更新 AccessibilityInputFilter 的FLAG_FEATURE_TOUCH_EXPLORATION(触摸浏览)属性置为 true。在 FLAG_FEATURE_TOUCH_EXPLORATION 模式下会创建一个 TouchExplorer 对象。AccessibilityInputFilter 继承了 InputFilter,对输入事件进行过滤,通过和 TouchExplorer 共同实现 TalkBack 模式下的触摸浏览手势。TouchExplorer 负责将普通触摸事件转换为触摸浏览手势,例如将 MotionEvent.ACTION_DOWN 事件转换为 MotionEvent.ACTION_HOVER_ENTER(悬停事件)。因此在 TalkBack 开启的情况下,用户单击 View 时,App 执行的是 ACTION_HOVER_ENTER 事件,双击 View 时才会执行 ACTION_DOWN 事件。2.触摸事件到 Activity 的传递过程在 Android 中,消息机制是 handler 机制,通过将消息封装到 Message 中,并将该消息发送到 handler 所在的 MessageQueue 中,通过 Looper 不断调用 MessageQueue 的 next 方法进行消息的处理。当用户触摸屏幕上的某个 View 时,handler 会对收到的消息进行以下的处理:这里需要重点看一下 View 的 dispatchPointerEvent() 方法:publicfinalbooleandispatchPointerEvent(MotionEventevent){if(event.isTouchEvent()){returndispatchTouchEvent(event);}else{returndispatchGenericMotionEvent(event);}}在该方法中对 event 进行判断,如果是 touchEvent 就调用 dispatchTouchEvent() 方法,否则调用 dispatchGenericMotionEvent() 方法。判断是否为 touch 事件的逻辑如下:boolMotionEvent::isTouchEvent(int32_tsource,int32_taction){if(source&AINPUT_SOURCE_CLASS_POINTER){//SpecificallyexcludesHOVER_MOVEandSCROLL.switch(action&AMOTION_EVENT_ACTION_MASK){caseAMOTION_EVENT_ACTION_DOWN:caseAMOTION_EVENT_ACTION_MOVE:caseAMOTION_EVENT_ACTION_UP:caseAMOTION_EVENT_ACTION_POINTER_DOWN:caseAMOTION_EVENT_ACTION_POINTER_UP:caseAMOTION_EVENT_ACTION_CANCEL:caseAMOTION_EVENT_ACTION_OUTSIDE:returntrue;}}returnfalse;}符合以上 case 的 event 即为 TouchEvent。首先来看一下 dispatchPointerEvent 方法中对 TouchEvent 事件的处理,进入 DecorView 的 dispatchTouchEvent() 方法中:@OverridepublicbooleandispatchTouchEvent(MotionEventev){finalWindow.Callbackcb=mWindow.getCallback();returncb!=null&!mWindow.isDestroyed()&mFeatureId
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2025-1-13 08:03 , Processed in 0.499729 second(s), 26 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表