Accessibility测试:让每个用户都能用好你的App
说实话,我刚入行那会儿,对无障碍测试基本是忽视的。总觉得「我的App功能做完了,UI也挺好看,谁会在意那些看不见摸不着的东西?」直到有一次,我亲眼看到一个视障朋友拿着手机,用TalkBack艰难地操作一个我参与开发的App——那个体验,真的让我脸红。
从那以后,Accessibility测试就成了我项目里的必修课。今天我们就来聊聊,Android上怎么做无障碍测试。
Accessibility测试工具
工欲善其事,必先利其器。Android官方给我们准备了不少好用的工具。
1. Accessibility Scanner
这是Google官方出品的扫描工具。你不需要写一行代码,安装后打开App,它会自动扫描当前界面,然后告诉你哪里有问题。
我个人习惯在每次UI改动后,先用它扫一遍。它能检测出:
- 内容描述缺失
- 触摸目标太小
- 颜色对比度不足
- 点击区域重叠
安装方式很简单,Google Play搜「Accessibility Scanner」就行。或者你也可以用adb安装:
adb install -r AccessibilityScanner.apk
2. Espresso的AccessibilityChecks
如果你已经在用Espresso做UI测试,那加上无障碍检查几乎零成本。只需要在测试类里加一行:
@Before
fun setUp() {
AccessibilityChecks.enable()
}
然后你所有的Espresso测试,都会自动检查无障碍问题。我在项目中遇到过,加了这行之后,原本通过的测试突然红了——原来是一个ImageView忘了加contentDescription。
3. 系统自带的TalkBack
TalkBack是Android系统内置的屏幕阅读器。你可以在「设置 → 无障碍 → TalkBack」里打开它。我建议每个开发者都亲自体验一下,关掉屏幕,只用听的方式操作你的App。那种感觉,嗯,会让你对无障碍有全新的认识。
测试内容描述
内容描述,说白了就是给那些「看不见」的用户,用语音告诉他们「这是什么」。
比如一个图片按钮,你光放个图标,视障用户摸到它,TalkBack只会说「按钮」。这谁听得懂?
正确的做法是:
<ImageButton
android:id="@+id/btn_search"
android:src="@drawable/ic_search"
android:contentDescription="搜索" />
在代码里也可以动态设置:
btn_search.contentDescription = "搜索"
这里有个坑,我曾经踩过:
那什么时候不需要contentDescription?
- 纯装饰性的图片,比如分割线、背景图
- 已经有文字标签的控件
对于装饰性图片,可以这样处理:
android:contentDescription="@null"
或者用代码:
imageView.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
测试焦点导航
焦点导航,就是用户用键盘、方向键或者滑动屏幕时,焦点怎么移动。
你想想看,一个视障用户用手指在屏幕上滑动,TalkBack会逐个朗读焦点所在的元素。如果焦点顺序乱了,用户听到的内容就是东一句西一句,完全没法理解。
默认情况下,Android会按照布局的先后顺序决定焦点顺序。但有时候,这个顺序并不合理。
比如一个登录界面,布局顺序是:
- 用户名输入框
- 登录按钮
- 密码输入框
- 忘记密码链接
这显然不合理。用户应该先输入用户名,再输入密码,然后点击登录。这时候就需要手动调整焦点顺序:
<EditText
android:id="@+id/et_username"
android:nextFocusDown="@id/et_password"
android:nextFocusForward="@id/et_password" />
<EditText
android:id="@+id/et_password"
android:nextFocusDown="@id/btn_login"
android:nextFocusForward="@id/btn_login" />
<Button
android:id="@+id/btn_login"
android:nextFocusDown="@id/tv_forgot_password"
android:nextFocusForward="@id/tv_forgot_password" />
在代码里也可以设置:
et_username.nextFocusDownId = R.id.et_password
et_password.nextFocusDownId = R.id.btn_login
测试触摸目标大小
Android官方建议,触摸目标的最小尺寸是48dp(约9mm)。为什么是48dp?因为这是人类手指的平均接触面积。
如果按钮太小,用户很难点中。特别是对于手抖的老年人,或者运动障碍的用户,小按钮简直就是噩梦。
我曾在项目中遇到过,一个删除按钮只有32dp,用户反复点不到,气得直接卸载了App。后来我们改成了48dp,投诉就没了。
检查触摸目标大小,可以用Accessibility Scanner,它会自动标记出小于48dp的控件。也可以手动计算:
// 检查按钮是否满足最小触摸目标
fun checkTouchTargetSize(view: View): Boolean {
val width = view.width
val height = view.height
val minSize = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 48f,
view.resources.displayMetrics
).toInt()
return width >= minSize && height >= minSize
}
如果因为布局限制,按钮确实做不大,怎么办?
可以用padding来扩大触摸区域:
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:minWidth="48dp"
android:minHeight="48dp" />
这样按钮看起来可能不大,但触摸区域是够的。
测试颜色对比度
颜色对比度,说白了就是文字和背景之间的颜色差异够不够大。对比度不够,视力不好的用户就看不清。
WCAG 2.0标准要求:
| 类型 | 最小对比度 | 增强对比度 |
|---|---|---|
| 普通文本(小于18px) | 4.5:1 | 7:1 |
| 大文本(大于等于18px加粗,或24px普通) | 3:1 | 4.5:1 |
| 图标和图形 | 3:1 | 3:1 |
怎么计算对比度?有个公式:
对比度 = (L1 + 0.05) / (L2 + 0.05)
其中 L1 是较亮颜色的相对亮度
L2 是较暗颜色的相对亮度
当然,你不需要手动算。Accessibility Scanner会帮你算好。或者用在线工具,比如WebAIM的对比度检查器。
我建议在主题设计阶段就把对比度考虑进去。比如你的App主色调是浅蓝色,那文字就不能用白色,得用深色。否则对比度肯定不够。
好了,这一章的内容就到这里。记住,无障碍测试不是一次性的工作,而是应该融入你的日常开发流程。每次提交代码前,花5分钟跑一遍Accessibility Scanner,你会发现很多问题在早期就被解决了。