| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 移动开发 -> 抖音Android无障碍开发知识总结 -> 正文阅读 |
|
[移动开发]抖音Android无障碍开发知识总结 |
抖音无障碍背景国家近期开展了无障碍建设活动。为了积极响应国家号召,为抖音视障用户能够得到更好的交互体验,对抖音无障碍功能进行了专项治理和改造。 无障碍模式下的使用方法抖音的无障碍功能实现主要是通过开启 Google TalkBack(或第三方屏幕阅读)功能,将用户在屏幕上触摸选中区域的内容朗读出来,使得视障人士可以根据朗读的内容获取自己当前操作区域的信息,从而提升视障人士的使用和交互体验。 常用的操作手势:
本文的目的使研发同学对无障碍功能有一个更加全面的认识和了解,方便研发同学进行无障碍功能的开发。 本文将分为无障碍功能实现原理和无障碍功能实现实例两部分进行介绍。 无障碍功能实现原理系统结构无障碍功能的实现需要以下三个部分的支持:辅助 App(例如 TalkBack)、被辅助 app(用户使用的 app,例如抖音头条等)以及系统服务 AccessibilityManagerService,这三者之间的关系如下图所示: 从上图中可以看出,以上的流程主要涉及到三个进程的通信。辅助 app 和被辅助 app 不需要直接跟被辅助的 app 通信,而是通过 SystemServer 进行中转通信,这个过程主要涉及到了四个 aidl 接口:
当被辅助 app 产生触摸事件后,会通过该接口发送无障碍事件给 SystemServer 进程的 AccessibilityManagerService。
当 SystemServer 接收到被辅助 app 发送的无障碍事件时,会将事件通过该接口传递给辅助 app(例如 TalkBack)进行处理。
当需要被辅助 app 的某个 View 的信息时,可以通过这两个接口的 findAccessibilityNodeInfosByViewId 方法实现。 无障碍事件传递流程当用户触摸屏幕时,会经过以下的流程将触摸事件传递给被触摸的 View: 下面本文将主要分析以上流程中四个重点部分的内容:无障碍模式下的事件转换、触摸事件到 Activity 的传递过程、事件传递给具体的 View 的分发过程以及最终无障碍事件的执行流程。 1.无障碍模式下的事件转换在 TalkBack 开启的状态下,由于 TalkBack 的无障碍服务中声明了 android:canRequestTouchExplorationMode=''true'' ,因此开启 TalkBack 后 AccessibilityManagerService 会更新 AccessibilityInputFilter 的 在 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() 方法:
在该方法中对 event 进行判断,如果是 touchEvent 就调用 dispatchTouchEvent() 方法,否则调用 dispatchGenericMotionEvent() 方法。判断是否为 touch 事件的逻辑如下:
符合以上 case 的 event 即为 TouchEvent。 首先来看一下 dispatchPointerEvent 方法中对 TouchEvent 事件的处理,进入 DecorView 的 dispatchTouchEvent() 方法中:
在该方法中,mWindow 是与 Activity 关联的 PhoneWindow 对象,由于 DecorView 是由 PhoneWindow 创建的,并且通过 setWindow() 方法,DecoView 对象持有 PhoneWindow 对象的引用。通过 getCallback() 方法,获得了实现了 Window.Callback 的对象,而 Activity 实现了这个接口,因此当调用cb.dispatchTouchEvent(ev) 时,实际上调用的是 Activity 中的 dispatchTouchEvent() 方法。 同样的在 dispatchGenericMotionEvent() 方法中,也有类似的代码逻辑:
此方法中实际上也是调用了 Activity 的 dispatchGenericMotionEvent() 方法对事件进行后续的分发和处理。此时事件就已经传递到了 Activity,由 Activity 进一步进行事件分发。 3.触摸事件传递到具体 View 的过程在研究无障碍模式下的事件传递过程之前,首先来回顾一下普通模式下的事件传递机制: 3.1 普通模式的事件分发3.1.1 普通模式下事件分发 Key Method 当一个 MotionEvent 产生之后,系统需要将该事件传递给一个具体的 view,这个传递过程就是事件的分发过程。分发过程依赖于以下三个重要方法:
该方法用来进行事件的分发,方法的返回值取决于当前 View 的 onTouchEvent() 方法和子 View 的 dispatchTouchEvent() 方法的影响。
仅 ViewGroup 拥有的方法,用来判断是否拦截某个事件。
在 dispatchTouchEvent() 方法中进行调用,用来处理点击事件。 3.1.2 普通模式下的事件分发 整个分发过程可以用以下的流程图来表示: 3.2 无障碍模式下的事件分发无障碍模式下的事件分发与普通模式下的事件分发有很多相似之处: 3.2.1 无障碍模式下的事件分发 Key Method: 与普通事件触摸事件的分发类似,无障碍事件触发事件分发也有类似的三个重要方法:
该方法用来进行事件的分发,方法的返回值取决于当前 View 的 onHoverEvent() 方法和子 View 的 dispatchHoverEvent() 方法的影响。
仅 ViewGroup 拥有的方法,用来判断是否拦截某个事件。
在 dispatchHoverEvent() 方法中进行调用,用来处理 hover 事件。 3.2.2 无障碍模式下的事件分发 当用户处于无障碍模式下,用户进行点击屏幕时,会调用 dispatchPointerEvent 方法中的 dispatchGenericMotionEvent 方法:
实际上调用的是 Activity 的 dispatchGenericMotionEvent() 方法,Activity 接收到事件后,会传递给 PhoneWindow 再传递给 DecorView。DecorView 会调用 View 的 dispatchGenericMotionEvent() 方法:
在该方法中,如果判断事件为 HoverEvent,就调用 ViewGroup 的 dispatchHoverEvent() 方法开始进行事件分发。 如果某个 ViewGroup 的 onInterceptHoverEvent() 方法返回 true,表示它要拦截当前事件,并交给自己处理,反之返回 false 表示不拦截当前事件,并将当前事件继续传递给子 View,子 View 会调用自己的 dispatchHoverEvent() 方法,如此循环往复直到事件最终被处理。 在事件处理阶段,View/ViewGroup 首先会判断是否设置了 OnHoverListener,并判断它的 onHover 方法的返回值是否为 true,如果返回值为 true,则不会调用 onHoverEvent() ,反之会调用 onHoverEvent() 方法对事件进行处理。 整个处理过程可以用下面的流程图进行表示: 在 onHoverEvent() 方法中,会调用到 sendAccessibilityHoverEvent()方法,该方法后续会调用以下方法:
以上 6 种方法为当自定义 View 时适配无障碍模式可以覆盖实现的方法,可以重写 View 的这些方法或者实现 View.AccessibilityDelegate 来解决一些特殊场景下 TalkBack 播报的问题。 其中的 sendAccessibilityEventUnchecked 方法会向上传递到 ViewRootImpl 的 requestSendAccessibilityEvent 方法中,从堆栈信息中就可以证实这一点: 接着无障碍事件会通过 AccessibilityManager 的 sendAccessibilityEvent 方法跨进程调用 system_process 进程的 AccessibilityManagerService,将 AccessibilityEvent 事件传递到 TalkBack 的 TalkBackService 中。 4.无障碍事件的执行流程这一节主要分析从 TalkBack 发出无障碍事件,到被辅助 app 在屏幕上绘制出绿框的过程。 TalkBack 将无障碍事件发送给被辅助 APP 时,需要 system_process 进程作为中转,对应的接口为 IAccessibilityServiceConnection.aidl 和 IAccessibilityInteractionConnection.aidl。经过中转后,最终会调用到被触摸 View 的 performAccessibilityAction 方法中,在没有 delegate 的情况下,会执行 performAccessibilityActionInternal 方法。在该方法中,如果是 ACTION_ACCESSIBILITY_FOCUS 事件,会执行 requestAccessibilityFocus 方法: 这个方法会执行两个关键操作:
无障碍功能实现实例
解决方案:在该 View 的 android:contentDescription 属性上设置需要播报的 String。
解决方案:将不需要播报的 View 的 android:importantForAccessibility 属性设置为 no,将需要播报的 View 的该属性设置为 yes。
解决方案:将下层的根 View 的 android:importantForAccessibility 属性设置为"noHideDescendants"
解决方案:在自定义 Toast 展示的时候,主动发送一个 AccessibilityEvent 事件
设置延时是为了避免不生效的问题。
解决方法:override View 的 onPopulateAccessibilityEvent()方法。 举例:设置自定义 View 开/关状态(已开启/已关闭)的播报内容。
解决方法:使用 AccessibilityDelegate
加入我们欢迎加入抖音-关系与服务团队,我们专注于抖音多个核心业务场景的落地与迭代,在业务、架构、技术等方面都有投入,期待你的加入! 抖音-关系与服务团队正在热招 Android & iOS 研发,在北京,成都均有职位,欢迎投递简历!
点个在看杀个 Bug ? |
|
移动开发 最新文章 |
Vue3装载axios和element-ui |
android adb cmd |
【xcode】Xcode常用快捷键与技巧 |
Android开发中的线程池使用 |
Java 和 Android 的 Base64 |
Android 测试文字编码格式 |
微信小程序支付 |
安卓权限记录 |
知乎之自动养号 |
【Android Jetpack】DataStore |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/23 11:05:22- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |