人文关怀:无障碍功能最佳实践

共 8637字,需浏览 18分钟

 ·

2022-03-04 10:26

前言

本文主要总结了常见的Android无障碍适配场景&适配技巧,便于你快速进行无障碍的功能适配。


什么是无障碍功能

我国现在 1691万 视障人士,2780万 听障人士,2977万 肢体残障人士,数据来源于 「2021年第7次」 人口普查。

对于一些视障/听障人群而言,如需正常使用App功能需借助系统附带的屏幕阅读器 TalkBack 来进行辅助使用。

TalkBack则是集成无障碍功能和服务来提高应用对于视障/听障人群的易用性


官方支持

Google 官方对于适配无障碍功能有专门的网站及文档用于描述:

  • Android原生UI-构建无障碍功能更出色的应用
  • Compose - 对于无障碍的支持
  • GoogleIO-2021-无障碍分场

开发准则

在进行无障碍功能适配开发时,需遵循以下准则:

  • 遵循 Material 中的无障碍开发规则
  • 非Text 类的 View 增加合适的 contentDescription
  • 对于一些 装饰性的UI元素 去掉标签及焦点
  • EditText 通过 hint 设置标签
  • 比较复杂的页面采用 分组聚集 的方式
  • 「自定义」 的 View 进行无障碍适配

适配技巧

下面总结了常见的Android无障碍适配场景&适配技巧,便于你快速进行无障碍的功能适配。主要包括:

  • 为View增加描述
  • 使用ImageBtn替代Image
  • 改造非标准组件的选中状态
  • 手动发送无障碍事件
  • 增加按钮触摸范围
  • 处理焦点
  • 兼容自定义View
  • 无障碍适配测试

技巧1:为View增加描述

对于继承自 TextView 类的组件,Android框架本身可以读出文本的信息,所以一般情况下,我们无需再次手动适配,我们主要需要适配的是哪些Image 或者 「无法描述」 的一类组件。

如下所示,增加相应的描述即可。

// xml
android:contentDescription="xxx"

// kotlin
view.contentDescription="xxx"

技巧2:使用ImageBtn替代Image

对于按钮(「即可以交互的一些行为」),Android官方建议使用 ImageButton 替代普通的 ImageView 。相信不少同学在定义自己Bar时,肯定使用的 Image 作为返回按钮,这也是很常见的,但为什么官方建议大家使用 ImageButton 呢?

主要是因为在适配无障碍模式时,无障碍服务在读取到 Image 时,如果此时增加了描述信息,则会 「直接读出文本名字」 ,但如果此时这是一个可以交互的按钮呢?对于我们普通用户而言,大家知道这里可以点击,但是他们并不知道,所以在这里如果使用 ImageButton ,此时在无障碍下的反馈就是:xxxApp,返回 「按钮」。双击进入下一步

对于视障用户而言,这将提高他们的使用便利度,以方便他们的使用。


技巧3:改造非标准组件的选中状态

<img src="https://tva1.sinaimg.cn/large/008i3skNly1gynrjs3spvj30ia0a0jrl.jpg" alt="image-20220123180508158" style="zoom:33%;" />

类似上述代码,如果这里的选择框使用的是 ImageView 去定义,此时无障碍服务将无法识别当前相应的状态。

如果使用系统默认的组件,如 CheckBox 或者 Switch ,则可以正确读出相应状态,如果因为业务等相关问题无法直接调整,可以通过手动添加无障碍代理的方式,间接的为控件增加无障碍下的状态,如下代码所示:

view.accessibilityDelegate = object : View.AccessibilityDelegate() {
            override fun onInitializeAccessibilityNodeInfo(
                host: View?,
                info: AccessibilityNodeInfo?
            )
 {
                super.onInitializeAccessibilityNodeInfo(host, info)
                info?.isCheckable = true
                // 设置选择状态
                info?.isChecked = isSelect
            }
        }

技巧4:手动发送无障碍事件

但某些情况下,我们不可能每次都像上述那要去设置吧,每次点击开关时,都走一遍上述设置代理的逻辑吧,那的确挺不优雅的,比如在某个 「RecyclerView」 中,我们甚至总不能自己维护一个选择状态吧。那么有没有其他方式,当我点击开关时,「手动去通知」 更新当前无障碍下的 [选择] 状态呢?

当然是可以的,我们通过 View.sendAccessibilityEvent(type) 手动发送一个通知事件,支持的事件类型如下:

// 具体在 AccessibilityEvent 类中
    public static final int TYPE_VIEW_CLICKED = 0x00000001;
    public static final int TYPE_VIEW_LONG_CLICKED = 0x00000002;
    public static final int TYPE_VIEW_SELECTED = 0x00000004;
    public static final int TYPE_VIEW_FOCUSED = 0x00000008;
    public static final int TYPE_WINDOWS_CHANGED = 0x00400000;
    ...

「使用方式示例:」

比如我们使用 「view」.sendAccessibilityEvent(TYPE_VIEW_SELECTED),那么就可以在自定义的无障碍代理 onPopulateAccessibilityEvent() 方法中获得监听,从而更改配置,具体如下所示:

setAccessibilityDelegate(new AccessibilityDelegate() {
            @Override
            public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
                super.onPopulateAccessibilityEvent(host, event);
                int type = event.getEventType();
               // type取决于你sendAccessibilityEvent(type)时
                if (type == AccessibilityEvent.TYPE_VIEW_SELECTED || type == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED) {
                   //提示,可以使用host.getTag(),将数据保存在view的tag中
                    event.getText().add("复选框" + "自定义逻辑");
                }
            }
        });

技巧5:增加按钮触摸范围

在MD的设计中,按钮的可触摸范围至少为 「48dpx48dp」 ,所以如果我们的按钮大小不足,则可以使用下述方向进行优化:

  • 使用 padding 为按钮图标周围增加填充
  • 使用 touchDelegate ,具体详见 读源码长知识 | 原来可以这样扩大 View 点击区域

技巧6:处理焦点

对于部分 View ,我们可能并不想在无障碍下被读取,或者需要将一些view进行 合并 ,此时就可以为其增加 importantForAccessibility 属性,默认提供了以下几个选项:

<attr name="importantForAccessibility" format="integer">
            <!--默认行为,系统根据其子view的类型自动判断是否对其进行读取-->
            <enum name="auto" value="0" />
            <!--允许在无障碍模式下访问-->
            <enum name="yes" value="1" />
            <!--禁止在无障碍模式下访问-->
            <enum name="no" value="2" />
            <!--表示其子view都禁止无障碍模式下访问-->
            <enum name="noHideDescendants" value="4" />
</attr>

「示例:」

<img src="https://tva1.sinaimg.cn/large/008i3skNly1gymtv7xhugj30ic0c63z3.jpg" alt="image-20220122223943594" style="zoom:50%;" />

比如上述示例,如果没加特别描述,默认情况下,点击时,红框里只会有 「微信好友(TextView)」 在无障碍模式下触发,这显然不是我们希望的,此时就可以通过将 contentDescription 上移到外部红框的 ViewGroup 中。相应的,某些业务规则下,如果并不想其在无障碍下被选中,比如 [微博] 此时如果没有安装,则可以 「忽略其焦点」「禁用」 在无障碍下的可访问性:

// xml
android:focusable="false" 
android:focusableInTouchMode="false" 
android:importantForAccessibility="no"

// kotlin
view.isFocusable = false
view.isFocusableInTouchMode = false
view.importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_NO

技巧7:兼容自定义View

我们的项目中多多少少都会存在很多自定义View,有些是基于View,有些是基于 ViewGroup 的组合效果。那么如何对自定义View做兼容呢?只需要增加相应的 contentDescription 即可,相应所有View页都可以去调用 「setContentDescription()」

需要注意的是,如果你自定义的 View 是一个 「按钮」 功能 ,而且没有继承自 ImageButton 或者 Button ,那么此时如果只是增加 contentDescription 标签描述,那么当view在无障碍下点击时,则只会读取描述,而使用了 ImageButton 或者 Button 的在无障碍模式下会被读作xx 「[按钮]」 ,相比起来,后者更象征着这具有一个行为作用,而前者仅仅像一个普通文本,这对视障用户而言,体验将影响很多。

「所以我们要如何快速的兼容呢?」如果你注意观察ImageButton与Image之间的区别,你就会发现

<img src="https://tva1.sinaimg.cn/large/008i3skNly1gyklxo0x25j30ze0u00xs.jpg" alt="image-20220121003411236" style="zoom:33%;" />

「getAccessibilityClassName()」,我们只需要返回相应的 Class Name 即可。所以如果你的某个 View 具有 「行为」 作用,或者代表着是一个自定义的 「按钮」 ,那么就可以重写你所自定义View的这个方法,返回 Button ,或者 ImageButton ,这样在无障碍模式下,其就会被系统判断为是一个具有交互作用的按钮。

更多关于自定义View的适配,可以查看Android官方文档-让自定义视图使用起来更没有障碍,里面主要是讲了通过无障碍代理类来实现。


技巧8:测试无障碍适配

相应的,Google 也提供了一些工具用于查看你的适配是否合理。如无障碍功能扫描仪使用文档。

无障碍功能扫描仪主要用于:对当前屏幕上所有的 View 进行扫描,并给出建议,主要包括:

  • 内容标签
  • 触目目标的尺寸
  • 是否存在可点按的内容
  • 文本和图片的对比度

我们可以用其作为一个参考作用来使用。如:

<img src="https://tva1.sinaimg.cn/large/008i3skNly1gyns2o3bnaj317w0oq0y3.jpg" alt="image-20220123182316744" style="zoom:33%;" />

其会自动将一些认为可以优化的 View 标注出来,有些是触摸按钮太小,有些是对比度不够,在开发过程中,我们可以借此来实现快速调整。

本文主要来自读者投稿:Peterp,原文链接:https://juejin.cn/post/7056349393879990286


「Carson每天带你学习一个Android知识点」,点击下方关注;期待您文章的投稿:真诚邀请您来分享

最后福利:学习资料赠送

  • 福利:本人亲自整理的「Android学习资料」
  • 数量:10名
  • 参与方式:「点击文章右下角”在看“并回复截图到公众号,将随机抽取」

    点击就能升职、加薪水!
浏览 78
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐