腾小云导读
现我国现有4471w视障/听障人士,60岁及以上人群达2.6亿规划。微信作为国民级运用,完成无障碍迫在眉睫。为了协助他们更好地运用微信 App,Android微信完成了适老化及无障碍改造。本文首要介绍Android 微信开发团队依据适老化及无障碍需求,完成的一个协助业务侧进行无障碍功用开发的结构。希望能给广阔开发爱好者带来协助和启示!
看目录,点收藏
1 无障碍需求结构布景
1.1 无障碍需求
1.2 结构简介
2 无障碍开发基础知识
2.1 读屏软件辨认View原理
2.2读屏软件后的事情分发原理
3 结构完成的全体流程和履行原理
3.1 全体流程
3.2 履行原理
4 中心阐明:大局热区补足机制
4.1布景阐明
4.2详细完成
4.3 额定阐明
5走查东西
6 总结
01、无障碍需求结构
现在,业界已经有一致性的无障碍开发守则。例如 Web Content Accessibility Guidelines (WCAG) 2.0,它是由互联网的首要国际标准组织万维网联盟 (W3C) 的Web可访问性建议 (WAI) 发布的一系列 Web 可访问性指南的一部分。
此外,WAI-ARIA(可访问的富Internet运用程序套件)是由万维网联盟(W3C)发布的一项关于 A11 Y技能运用标准。该标准界说了一种使残障人士更易于访问 Web 内容和 Web 运用程序的办法,增加 HTML、JavaScript 和相关技能开发的网站动态内容以及用户界面组件的可访问性。
现在,Android没有官方一致、便利的结构,官方供给的原生api并不是特别好用,所以微信团队对其进行参考,开发了一个无障碍结构,依据原生的api进行了再封装,将繁琐的无障碍适配逻辑封装在底层,以声明式接口的形式,让上层业务能以更简便更解耦的代码,完成无障碍的适配。接下来咱们进行共享:
1.1无障碍需求
本结构首要具有以下特性:
- 可感知性 :包含大字体适配,颜色对比度等 。
- 可操作性 :首要是过小热区的扩展,提高老年人/残疾人的交互体会 。
- 可了解性 :微信应供给读屏案牍等信息,协助盲人在敞开 Talkback 等读屏软件的状况下,正常运用微信。
下面给出一些较为典型的需求:
- 需求1:过小热区的扩展
需求是要求微信内的一切可交互控件,可点击规划不得低于 44dp * 44dp。
巨细不合规的控件,假如一个个进行排查、布局修正。工程量巨大。
- 需求2:呼应区域会随无障碍开关产生改变
该 Item 由一个 SwitchButton + TextView 组成。
敞开 Talkback 时,整个 Item 辨认为一个焦点,选中双击是触发点击 switch的逻辑。在无障碍形式下,选中双击是直接触发相应控件的 Click 事情。可是在不开 Talkback 的状况下点击 Item 又无需呼应,只呼应 SwitchButton 。也便是点击区域会随 Talkback 开关产生改变。
完成或许是:在 ItemClick 中进行 if 判别。但这样写侵入性高,难维护。
- 需求3:读屏案牍由其他的控件的值组合
选中头像,读屏案牍:腾讯行政的头像,有 2 条未读消息。需求读出列表中其他关联内容,这种只能把适配代码侵入到 Adapter中。
1.2 结构简介
结构将不同的无障碍需求的完成进行封装,抽象成不同的规矩。
业务侧可以将一个页面/业务的无障碍需求,在一个装备类里运用规矩表达出来,再由结构进行处理。完成相应的作用。
classChatAccessibility(activity: AppCompatActivity) :
BaseAccessibilityConfig(activity) {
overridefuninitConfig() {
// 设置 contentDesc
view(rootId,viewId).desc(R.string.send_smiley)
// ...
}
}
结构基类 BaseAccessibilityConfig 供给了一系列用于表达规矩的 api,包含但不限于如下功用:
-
经过装备一致设置 contentDescription
-
支撑把多个 View 组合成一体进行读屏
-
经过装备禁用某个View被 Talkback 集合的能力
-
支撑按指定次序进行读屏,支撑部分操控 Talkback 集合次序
-
支撑设定在 Activity 启动后的第一个读屏控件
-
支撑对某个父 View 的 disableChildren 功用
-
在某个 View 满意条件时,对其进行读屏,但不集合
-
在某个 View 满意条件时,读出提早设定的 string,但不集合
-
大局热区宽高补齐至 44dp,并供给自界说热区扩展/禁用热区扩展的功用 …
02、无障碍开发基础知识
在深化了解结构的规划前,先来介绍一些无障碍功用开发的基础知识。
2.1 基础知识1:读屏软件辨认 View 原理
读屏软件无法直接辨认到View,只能辨认到View供给的虚拟节点「Node」,View 和虚拟节点一般是一一对应的。当页面内容产生改变,比方 View 被设值,或者产生翻滚等状况,View 会向无障碍体系发送一个事情,告诉体系。
然后体系就回头向 View 索取节点,组成页面更新后新的节点树,而 「节点树 和 ViewTree 是一一对应的」。此时读屏软件拿到的便是新的内容了。
2.2 基础知识2:读屏软件后的事情分发流程
分为上下两部分:读屏软件阻拦处理行为、读屏软件接受事情。
流程如下:
-
读屏软件阻拦用户 Touch 事情,依据事情的坐标去定位到目标节点。
-
将 Touch 事情解释为节点行为,这里以接触选中为例,那么便是集合行为。
-
读屏软件经过该节点向无障碍体系发送,无障碍体系又转发给View(集合产生的绿框便是在View的内部处理里去制作的)。
-
生成新的虚拟节点并供给给读屏软件后,读屏软件组合信息,经过 TTS 语音引擎的 api 读出。
读屏软件展现给用户的一切信息,悉数来自虚拟节点。可以在节点生成的过程中,修正节点的信息,所以这里是一个绝佳的**「信息自界说」**的当地。
选用将一切的 View 都「Wrap 一层 AccessibilityDelegate」的方式,「在 onInitializeAccessibilityNodeInfo 办法中修正节点信息」。
03、结构完成全体流程与履行原理
3.1 全体流程
-
业务侧完成规矩装备类,编写的规矩会进入装备池。
-
结构在View生成节点给体系的时分进行阻拦 「(onInitializeAccessibilityNodeInfo)」。
-
在装备池中寻找匹配的规矩。
-
依据匹配的规矩对节点进行修正。
-
终究生成的节点就会由体系交由给读屏软件进行读屏。
3.2 履行原理
中心原理:选用依据职责链的流水线来处理。全体流程首要分为两部分:
-
View 预处理职责链(图示左边):履行预出来操作,如异步生成缓存、View符号等;
-
节点处理职责链(图示右边):节点处理的一起会同步查找规矩进行设置。
接下来首要简略介绍下结构的一个中心功用的完成:「大局热区补足机制」 (坐落结构流程中的预处理职责链中的一环)。
04、中心阐明:大局热区补足机制
4.1 布景阐明
- 需求阐明
过小热区扩展,即微信内的一切可交互控件可点击规划不得低于 44dp * 44dp,像一些巨细不合规的控件,假如一个个进行排查、布局修正,工程量太巨大。还有热区其他一些需求 etc。
- 问题难点
一般会挑选直接修正 padding,有些乃至需求改动相应布局,但这样的改动工作量太大且容易影响本来视图布局。
- 处理计划
需求一个大局的热区补足机制,将过小热区补足至标准。
4.2 详细完成
在 「创立 View 的一致入口」 去设置 TouchDelegate 代理,由父 View 作为TouchDelegate 的承载 View 去代理 Touch 事情,这里有几个问题需求处理:
-
怎么找到合适的承载View
-
热区及时更新
-
性能优化
-
读屏形式下的热区扩展
下面咱们别离展开讲。
-
要点问题1:怎么找到合适的承载 View
从目标 View 向上冒泡,找到一个合适的父 View。那么需求 「冒泡终止条件」。 首要条件一肯定是 「足够大」。当时 View 够大了就没必要再往上冒了。
可是这样会存在问题:子 View 的 Click优先级高于父View的TouchDelegate。事情派发机制:
从父 View 往子 View 派发,从子 View 向上处理。View 的事情处理次序是先 OnTouchListener,然后是 TouchDelegate,再是Click、LongClick。
所以会导致下图的状况:
现在进行了折中处理,相比上图,显然是下图的扩展后的体会更佳:
一起加入了条件二:「该承载 View 是 Clickable、LongClickable」。终究计划流程确定如下:
-
要点问题2:热区及时更新
布景: 承载 View 的 TouchDelegate 需求的参数包含一个 Rect,也便是对扩展的热区进行呼应。
问题: 这个矩阵是提早传入,且和 小 View 没有直接的关系。假如小 View 的布局产生变动,会导致扩展后热区没有及时跟上改变。导致热区错位。
处理计划: 在 小 View 的 onLayoutChange 中重新进行一遍 View 扩展计划 的处理。一起为了避免 onLayoutChange 履行过于频频,将 onLayoutChange 包装成 View 的一个事情。假如短时间内屡次 onLayoutChange ,则只在终究一次 onLayoutChange 的时分进行 「View扩展计划」处理。
- 要点问题3:性能优化
布景 :开始的 View 扩展计划履行机遇是在创立 View 的一致入口,也便是在 LayoutInflate 的 onCreateView 中同步履行,每个 View 都得履行。
问题:由于 View 数量较为巨大,所以存在较大的性能危险。
处理计划:选用了异步计划并一起对 View 处理任务进行收拢。将履行机遇提早到 LayoutInflate.inflate 并异步处理,在异步任务中去遍历该 inflate 的根 View的一切子 View。尽量不去阻塞主线程的运行。
-
要点问题4:读屏形式下的热区扩展
经过上面的完成,点击热区确实是扩展了。可是在读屏形式下选中的时分,选中的框并没有扩展。那么首要需求知道,选中时的框是以什么作为 Bound。
绿框的制作中心逻辑坐落 ViewRootImpl 中的一个 drawAccessibilityFocusedDrawableIfNeeded(),该办法的调用机遇是用户接触选中某个View后,传递到 ViewRootImpl 时进行调用,也便是读屏选中的绿框是由体系制作的,而不是由读屏软件制作的。从源码中能够得知的是,绿框的Bound 依据是否有虚拟节点,分为两种状况:
privatevoiddrawAccessibilityFocusedDrawableIfNeeded(Canvas canvas){
finalRect bounds = mAttachInfo.mTmpInvalRect;
if(getAccessibilityFocusedRect(bounds)) {
finalDrawable drawable = getAccessibilityFocusedDrawable();
if(drawable !=null) {
drawable.setBounds(bounds);
drawable.draw(canvas);
}
}elseif(mAttachInfo.mAccessibilityFocusDrawable !=null) {
mAttachInfo.mAccessibilityFocusDrawable.setBounds(0,0,0,0);
}
}
privatebooleangetAccessibilityFocusedRect(Rect bounds){
...
finalAccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
if(provider ==null) {
host.getBoundsOnScreen(bounds,true);
}elseif(mAccessibilityFocusedVirtualView !=null) {
mAccessibilityFocusedVirtualView.getBoundsInScreen(bounds);
}else{
returnfalse;
}
...
return!bounds.isEmpty();
}
经过盯梢源码发现,这是由于 「绿框的制作」 是依据 View.getBoundInScreen 获取的矩阵来做到的。而 TouchDelegate 的设置无法改变 View.getBoundInScreen 获取到的矩阵。在运用虚拟节点的状况下,才会运用虚拟节点的Bound进行制作。
对于这个问题,咱们的处理思路是:
-
对每个 View 设置自界说的 AccessibilityDelegate, 并完成其间的 getAccessibilityNodeProvider 办法。
-
假如判别 View 需求扩展,在 getAccessibilityNodeProvider 中回来自界说的 Provider。
-
在自界说的 Provider 中,计算 View 的扩展后的矩阵在屏幕上的位置。
-
将矩阵设置给虚拟节点,并回来给体系。
4.3 额定阐明
- 怎么匹配规矩与View?
结构将装备池按 Activity 划分,极大削减抵触概率,一起削减装备池巨细,加速查找规矩的速度,供给 layoutId + viewId,rootId + viewId 两种形式的 View 定位机制。由两个 Id 确定一个 View,削减抵触。
- 查找规矩时间长或许导致的主线程卡顿?
由于查找规矩的机遇是在生成节点,是由体系触发且无法异步。在查找规矩的过程中,运用预处理的时分提早生成的缓存进行查找,尽或许削减耗时。
05、走查东西
5.1 布景
当完成无障碍需求的开发后,需进行验证。在验证过程中发现敞开验证功率低下,需敞开读屏软件后,逐一元素验证。
5.1.1 处理计划与原理
依据无障碍服务(AccessibilityService)开发、集成了在不敞开 Talkback 的状况下能展现读屏区域一个无障碍功用走查东西,无需敞开 Talkback 逐一手动接触,就能高效检查无障碍适配状况。
完成原理如下:
-
自界说完成一个 AccessibilityService 用于获取到当时活跃窗口的根节点。
-
每隔 0.5s 进行一次节点的获取:从当时活跃窗口的根节点遍历一切的节点,逐一进行判别是否会被集合。
-
对经过答应集合的节点进行信息搜集,在一次遍历完成后告诉到 DrawService。
-
提早在window中增加一个 View 用于制作信息,由 DrawService 进行制作。
5.2 详细完成
要害完成:怎么判别一个节点能否被集合,即需了解 Talkback 是怎么集合,流程如下:
1、假如是支撑 WebView 中 Html 无障碍,特殊判别。
2、假如不行见,则不集合。
3、判别是否是画中画,像下图的红框这种便是画中画,假如是画中画,这个便是焦点。
4、该节点是否和 window 鸿沟重合等大。对于这种和 window 等大的节点,Talkback 挑选不做集合。
5、检查该节点是否 clickable/longClickable/focusable 或者是列表的“会说话的” 顶层视图(满意->6 不满意->7)列表(ListView/RecycleView)的顶层视图例子如下:
可是集合的前提是“会说话的”。“会说话的”包含以下几个条件:
-
HasText:包含 contentDescription、text、hintText(包含 Button 的 Text)。
-
hasStateDescription:包含 CheckBox 的已选未选状况、进展条的进展状况等。
-
hasNonActionableSpeakingChildren:含有无法集合、点击可是 HasText 的子 View(如上图通讯录中的 “新的朋友” TextView,便是无法集合、点击可是 HasText 的子 View)。
6、基本上满意了过程5就可以视为可集合了,可是有一些View仅仅是 Focusable,可是却 ”什么话都没得说“ ,对于这种 View 应该是要排除的。故按如下过程做判别:只要是没有子节点的 focusable/clickable/longclickable 的 View,悉数集合 、“会说话的” 悉数集合 6.3 剩下的就不集合了(“不会说话”、“有子节点”)。
7、能到这一步,阐明过程 5 不满意,即该节点是一般的不行集合的 View。可是避免错过一些没有点击事情的 TextView 之类的需求集合,需求再终究做一步判别(这一步也是啥为了确保一切的信息都可以不遗漏);假如没有可集合父节点,但仍然 hasText 或 hasStateDescription,集合该节点。
8、一路闯关到这的 View,就总算逃离 TalkBack 的集合了。
06、总结
为了协助老年人、视障/听障人群等更好地运用微信 App,Android微信完成了适老化及无障碍改造如上。本文首要介绍 Android 微信开发团队依据适老化及无障碍需求,完成的一个协助业务侧进行无障碍功用开发的结构。咱们在介绍了无障碍开发所涉及的2大要点基础知识(读屏辨认View原理和读屏软件后的事情分发原理)之后,为各位展开回顾了咱们结构详细细节和办法。
以上是本次共享悉数内容,欢迎我们在谈论区共享沟通。假如觉得内容有用,欢迎转发~
-End-
原创作者|许怀鑫
技能责编|许怀鑫
现我国现有4471w视障/听障人士,60岁及以上人群达到2.6亿规划。信息无障碍(Web Accessibility)的概念在近几年遭到重视。 信息无障碍是指经过信息化手法弥补身体机能、所在环境等存在的差异,使任何人(无论是健全人仍是残疾人、无论是年轻人仍是老年人)都能相等、便利、安全地获取、交互、运用信息。微信、QQ、腾讯新闻和腾讯地图等运用加适老化元素,配备为老人而设的“关怀形式”;搜狗输入法推出为视障集体量身打造的“保益盲人输入法”……
当说到无障碍,我们第一反应是弱势集体。实际上,无障碍是适用于全民的。每个人都或许有遇障时间。当你手提重物或受伤时,你或许会挑选乘坐无障碍电梯;当你处在喧闹的环境下看视频时,你或许需求经过字幕获取信息……每个人都是无障碍环境的受益者,视障、听障人群、含残疾人、老年人是信息无障碍的要点受益集体。
事情共享:你还见到过哪些让你眼前一亮的信息无障碍案例?
脑洞时间:程序员还可认为信息无障碍做些什么?
欢迎在公众号谈论区聊一聊你的看法。在4月10日前将你的谈论记录截图,发送给腾讯云开发者公众号后台,可收取腾讯云「开发者春季限制红包封面」一个,数量有限先到先得。咱们还将选取点赞量最高的1位朋友,送出腾讯QQ公仔1个。4月10日中午12点开奖。快约请你的开发者朋友们一起来参与吧!
回复「微信」,收取更多微信的技能case和论文资源
阅读原文