无障碍
Android 和 iOS 都提供了 API,用于将应用与辅助技术(如捆绑的屏幕阅读器 VoiceOver (iOS) 和 TalkBack (Android))集成。React Native 拥有互补的 API,让你的应用能够容纳所有用户。
Android 和 iOS 的方法略有不同,因此 React Native 的实现可能因平台而异。
无障碍属性
accessible
当为 true 时,表示该视图可被屏幕阅读器和硬件键盘等辅助技术发现。请注意,这并不一定意味着该视图会被 VoiceOver 或 TalkBack 聚焦。造成这种情况的原因有很多,例如 VoiceOver 不允许嵌套无障碍元素,或者 TalkBack 选择聚焦某些父元素而不是该元素。
默认情况下,所有可触摸元素都是可访问的。
在 Android 上,accessible 将被转换为原生 focusable. 在 iOS 上,它转换为原生 isAccessibilityElement。
<View>
<View accessible={true} />
<View />
</View>
在上面的示例中,无障碍焦点仅适用于具有 accessible 属性的第一个子视图,而不适用于没有 accessible 的父级或兄弟元素。
accessibilityLabel
当视图标记为可访问时,最好在视图上设置 accessibilityLabel,以便使用 VoiceOver 或 TalkBack 的用户知道他们选择了什么元素。当选择关联元素时,屏幕阅读器会将此字符串语音化。
要使用它,请在你的 View、Text 或 Touchable 上将 accessibilityLabel 属性设置为自定义字符串:
<TouchableOpacity
accessible={true}
accessibilityLabel="Tap me!"
onPress={onPress}>
<View style={styles.button}>
<Text style={styles.buttonText}>Press me!</Text>
</View>
</TouchableOpacity>
在上面的示例中,TouchableOpacity 元素上的 accessibilityLabel 默认为 "Press me!"。该标签是通过连接所有文本节点子项(以空格分隔)构建的。
accessibilityLabelledBy Android
引用另一个元素 nativeID 用于构建复杂表单。
accessibilityLabelledBy 的值应与相关元素的 nativeID 匹配:
<View>
<Text nativeID="formLabel">Label for Input Field</Text>
<TextInput
accessibilityLabel="input"
accessibilityLabelledBy="formLabel"
/>
</View>
在上面的示例中,当聚焦于 TextInput 时,屏幕阅读器会宣布 Input, Edit Box for Label for Input Field。
accessibilityHint
无障碍提示可用于在仅凭无障碍标签不清楚时,向用户提供关于操作结果的额外上下文。
在你的 View、Text 或 Touchable 上为 accessibilityHint 属性提供自定义字符串:
<TouchableOpacity
accessible={true}
accessibilityLabel="Go back"
accessibilityHint="Navigates to the previous screen"
onPress={onPress}>
<View style={styles.button}>
<Text style={styles.buttonText}>Back</Text>
</View>
</TouchableOpacity>
在上面的示例中,如果用户在设备的 VoiceOver 设置中启用了提示,VoiceOver 将在标签后读取提示。在 iOS 开发者文档 中阅读更多关于 accessibilityHint 的指南。
在上面的示例中,TalkBack 将在标签后读取提示。目前,Android 上无法关闭提示。
accessibilityLanguage iOS
通过使用 accessibilityLanguage 属性,屏幕阅读器将理解在读取元素的 标签、值 和 提示 时使用哪种语言。提供的字符串值必须遵循 BCP 47 规范。
<View
accessible={true}
accessibilityLabel="Pizza"
accessibilityLanguage="it-IT">
<Text>🍕</Text>
</View>
accessibilityIgnoresInvertColors iOS
反转屏幕颜色是 iOS 和 iPadOS 中供色盲、低视力或视力障碍人士使用的无障碍功能。如果在此设置开启时你不希望反转某个视图(可能是照片),请将此属性设置为 true。
accessibilityLiveRegion Android
当组件动态变化时,我们希望 TalkBack 提醒最终用户。这是通过 accessibilityLiveRegion 属性实现的。它可以设置为 none、polite 和 assertive:
- none 无障碍服务不应宣布对此视图的更改。
- polite 无障碍服务应宣布对此视图的更改。
- assertive 无障碍服务应中断正在进行的语音以立即宣布对此视图的更改。
<TouchableWithoutFeedback onPress={addOne}>
<View style={styles.embedded}>
<Text>Click me</Text>
</View>
</TouchableWithoutFeedback>
<Text accessibilityLiveRegion="polite">
Clicked {count} times
</Text>
在上面的示例中,方法 addOne 更改状态变量 count。当触发 TouchableWithoutFeedback 时,TalkBack 会读取 Text 视图中的文本,因为其 accessibilityLiveRegion="polite" 属性。
accessibilityRole
accessibilityRole 向辅助技术用户传达组件的目的。
accessibilityRole 可以是以下之一:
- adjustable 当元素可以“调整”时使用(例如滑块)。
- alert 当元素包含要呈现给用户的重要文本时使用。
- button 当元素应被视为按钮时使用。
- checkbox 当元素代表可以选中、取消选中或具有混合选中状态的复选框时使用。
- combobox 当元素代表组合框时使用,允许用户在几个选项中进行选择。
- header 当元素充当内容部分的标题时使用(例如导航栏的标题)。
- image 当元素应被视为图像时使用。可以与按钮或链接组合。
- imagebutton 当元素应被视为按钮且也是图像时使用。
- keyboardkey 当元素充当键盘键时使用。
- link 当元素应被视为链接时使用。
- menu 当组件是选项菜单时使用。
- menubar 当组件是多个菜单的容器时使用。
- menuitem 用于代表菜单中的项目。
- none 当元素没有角色时使用。
- progressbar 用于表示指示任务进度的组件。
- radio 用于代表单选按钮。
- radiogroup 用于代表一组单选按钮。
- scrollbar 用于代表滚动条。
- search 当文本字段元素也应被视为搜索字段时使用。
- spinbutton 用于代表打开选项列表的按钮。
- summary 当元素可用于在应用首次启动时提供应用中当前条件的快速摘要时使用。
- switch 用于代表可以打开和关闭的开关。
- tab 用于代表标签页。
- tablist 用于代表标签页列表。
- text 当元素应被视为无法更改的静态文本时使用。
- timer 用于代表计时器。
- togglebutton 用于代表切换按钮。应与 accessibilityState checked 一起使用以指示按钮是切换打开还是关闭。
- toolbar 用于代表工具栏(操作按钮或组件的容器)。
- grid 与 ScrollView、VirtualizedList、FlatList 或 SectionList 一起使用以代表网格。将进出网格公告添加到 Android 的 GridView。
accessibilityShowsLargeContentViewer iOS
一个布尔值,确定当用户在该元素上执行长按时是否显示大内容查看器。
适用于 iOS 13.0 及更高版本。
accessibilityLargeContentTitle iOS
当显示大内容查看器时,将用作其标题的字符串。
需要将 accessibilityShowsLargeContentViewer 设置为 true。
<View
accessibilityShowsLargeContentViewer={true}
accessibilityLargeContentTitle="Home Tab">
<Text>Home</Text>
</View>
accessibilityState
向辅助技术用户描述组件的当前状态。
accessibilityState 是一个对象。它包含以下字段:
| Name | Description | Type | Required |
|---|---|---|---|
| disabled | 指示元素是否禁用。 | boolean | No |
| selected | 指示可选择元素当前是否被选中。 | boolean | No |
| checked | 指示可检查元素的状态。此字段可以采用布尔值或 "mixed" 字符串来表示混合复选框。 | boolean or 'mixed' | No |
| busy | 指示元素当前是否忙碌。 | boolean | No |
| expanded | 指示可展开元素当前是展开还是折叠。 | boolean | No |
要使用它,请将 accessibilityState 设置为具有特定定义的对象。
accessibilityValue
表示组件的当前值。它可以是组件值的文本描述,或者对于基于范围的组件(如滑块和进度条),它包含范围信息(最小值、当前值和最大值)。
accessibilityValue 是一个对象。它包含以下字段:
| Name | Description | Type | Required |
|---|---|---|---|
| min | 此组件范围的最小值。 | integer | 如果设置了 now 则为必填。 |
| max | 此组件范围的最大值。 | integer | 如果设置了 now 则为必填。 |
| now | 此组件范围的当前值。 | integer | No |
| text | 此组件值的文本描述。如果设置,将覆盖 min、now 和 max。 | string | No |
accessibilityViewIsModal iOS
一个布尔值,指示 VoiceOver 是否应忽略接收器兄弟视图内的元素。
例如,在包含兄弟视图 A 和 B 的窗口中,在视图 B 上将 accessibilityViewIsModal 设置为 true 会导致 VoiceOver 忽略视图 A 中的元素。另一方面,如果视图 B 包含子视图 C 并且你在视图 C 上将 accessibilityViewIsModal 设置为 true,VoiceOver 不会忽略视图 A 中的元素。
accessibilityElementsHidden iOS
一个布尔值,指示给定的无障碍元素及其包含的任何无障碍元素是否隐藏。
例如,在包含兄弟视图 A 和 B 的窗口中,在视图 B 上将 accessibilityElementsHidden 设置为 true 会导致 VoiceOver 忽略 B 视图及其包含的任何元素。这类似于 Android 属性 importantForAccessibility="no-hide-descendants"。
aria-valuemax
表示基于范围的组件(如滑块和进度条)的最大值。
aria-valuemin
表示基于范围的组件(如滑块和进度条)的最小值。
aria-valuenow
表示基于范围的组件(如滑块和进度条)的当前值。
aria-valuetext
表示组件的文本描述。
aria-busy
指示元素正在被修改,辅助技术可能希望等待更改完成后再告知用户更新。
| Type | Default |
|---|---|
| boolean | false |
aria-checked
指示可检查元素的状态。此字段可以采用布尔值或 "mixed" 字符串来表示混合复选框。
| Type | Default |
|---|---|
| boolean, 'mixed' | false |
aria-disabled
指示元素可感知但已禁用,因此不可编辑或以其他方式操作。
| Type | Default |
|---|---|
| boolean | false |
aria-expanded
指示可展开元素当前是展开还是折叠。
| Type | Default |
|---|---|
| boolean | false |
aria-hidden
指示元素是否对辅助技术隐藏。
例如,在包含兄弟视图 A 和 B 的窗口中,在视图 B 上将 aria-hidden 设置为 true 会导致 VoiceOver 忽略 B 元素及其子元素。
| Type | Default |
|---|---|
| boolean | false |
aria-label
定义标记交互式元素的字符串值。
| Type |
|---|
| string |
aria-labelledby Android
标识标记它所应用元素的元素。aria-labelledby 的值应与相关元素的 nativeID 匹配:
<View>
<Text nativeID="formLabel">Label for Input Field</Text>
<TextInput aria-label="input" aria-labelledby="formLabel" />
</View>
| Type |
|---|
| string |
aria-live Android
指示元素将被更新,并描述用户代理、辅助技术和用户可以从实时区域预期的更新类型。
- off 无障碍服务不应宣布对此视图的更改。
- polite 无障碍服务应宣布对此视图的更改。
- assertive 无障碍服务应中断正在进行的语音以立即宣布对此视图的更改。
| Type | Default |
|---|---|
enum('assertive', 'off', 'polite') | 'off' |
aria-modal iOS
布尔值,指示 VoiceOver 是否应忽略接收器兄弟视图内的元素。
| Type | Default |
|---|---|
| boolean | false |
aria-selected
指示可选择元素当前是否被选中。
| Type |
|---|
| boolean |
importantForAccessibility Android
在具有相同父级的两个重叠 UI 组件的情况下,默认无障碍焦点可能具有不可预测的行为。importantForAccessibility 属性将通过控制视图是否触发无障碍事件以及是否报告给无障碍服务来解决此问题。它可以设置为 auto、yes、no 和 no-hide-descendants(最后一个值将强制无障碍服务忽略组件及其所有子项)。
<View style={styles.container}>
<View
style={[styles.layout, {backgroundColor: 'green'}]}
importantForAccessibility="yes">
<Text>First layout</Text>
</View>
<View
style={[styles.layout, {backgroundColor: 'yellow'}]}
importantForAccessibility="no-hide-descendants">
<Text>Second layout</Text>
</View>
</View>
在上面的示例中,yellow 布局及其后代对 TalkBack 和所有其他无障碍服务完全不可见。因此我们可以使用具有相同父级的重叠视图而不会混淆 TalkBack。
onAccessibilityEscape iOS
将此属性分配给自定义函数,当有人执行“退出”手势(即双指 Z 形手势)时将调用该函数。退出函数应在用户界面中分层向后移动。这可能意味着在导航层次结构中向上或向后移动,或关闭模态用户界面。如果所选元素没有 onAccessibilityEscape 函数,系统将尝试遍历视图层次结构,直到找到具有该函数的视图,或发出提示表示无法找到。
onAccessibilityTap iOS
使用此属性分配自定义函数,当有人双击选中的可访问元素激活它时将调用该函数。
onMagicTap iOS
将此属性分配给自定义函数,当有人执行“魔术点击”手势(即双指双击)时将调用该函数。魔术点击函数应执行用户可在组件上采取的最相关操作。在 iPhone 上的电话应用中,魔术点击可接听电话或结束当前通话。如果所选元素没有 onMagicTap 函数,系统将遍历视图层次结构直到找到具有该函数的视图。
role
role 传达组件的目的,并优先于 accessibilityRole 属性。
role 可以是以下之一:
- alert 当元素包含要呈现给用户的重要文本时使用。
- button 当元素应被视为按钮时使用。
- checkbox 当元素代表可以选中、取消选中或具有混合选中状态的复选框时使用。
- combobox 当元素代表组合框时使用,允许用户在几个选项中进行选择。
- grid 与 ScrollView、VirtualizedList、FlatList 或 SectionList 一起使用以代表网格。将进出网格公告添加到 Android 的 GridView。
- heading 当元素充当内容部分的标题时使用(例如导航栏的标题)。
- img 当元素应被视为图像时使用。例如,可以与按钮或链接组合。
- link 当元素应被视为链接时使用。
- list 用于标识项目列表。
- listitem 用于标识列表中的项目。
- menu 当组件是选项菜单时使用。
- menubar 当组件是多个菜单的容器时使用。
- menuitem 用于代表菜单中的项目。
- none 当元素没有角色时使用。
- presentation 当元素没有角色时使用。
- progressbar 用于表示指示任务进度的组件。
- radio 用于代表单选按钮。
- radiogroup 用于代表一组单选按钮。
- scrollbar 用于代表滚动条。
- searchbox 当文本字段元素也应被视为搜索字段时使用。
- slider 当元素可以“调整”时使用(例如滑块)。
- spinbutton 用于代表打开选项列表的按钮。
- summary 当元素可用于在应用首次启动时提供应用中当前条件的快速摘要时使用。
- switch 用于代表可以打开和关闭的开关。
- tab 用于代表标签页。
- tablist 用于代表标签页列表。
- timer 用于代表计时器。
- toolbar 用于代表工具栏(操作按钮或组件的容器)。
无障碍操作
无障碍操作允许辅助技术以编程方式调用组件的操作。要支持无障碍操作,组件必须做两件事:
- 通过
accessibilityActions属性定义它支持的操作列表。 - 实现一个
onAccessibilityAction函数来处理操作请求。
accessibilityActions 属性应包含一个操作对象列表。每个操作对象应包含以下字段:
| 名称 | 类型 | 必填 |
|---|---|---|
| name | string | 是 |
| label | string | 否 |
操作要么代表标准操作,例如点击按钮或调整滑块,要么代表特定于给定组件的自定义操作,例如删除电子邮件消息。 name 字段对于标准操作和自定义操作都是必需的,但 label 对于标准操作是可选的。
当添加对标准操作的支持时, name 必须是以下之一:
'magicTap'- 仅限 iOS - 当 VoiceOver 焦点在组件上或组件内时,用户用两根手指双击。'escape'- 仅限 iOS - 当 VoiceOver 焦点在组件上或组件内时,用户执行了两指擦除手势(左、右、左)。'activate'- 激活组件。无论是否有辅助技术,这都应该执行相同的操作。当屏幕阅读器用户双击组件时触发。'increment'- 增加可调整组件的值。在 iOS 上,当组件具有'adjustable'角色且用户将焦点放在其上并向上滑动时,VoiceOver 会生成此操作。在 Android 上,当用户将无障碍焦点放在组件上并按下音量增加按钮时,TalkBack 会生成此操作。'decrement'- 减少可调整组件的值。在 iOS 上,当组件具有'adjustable'角色且用户将焦点放在其上并向下滑动时,VoiceOver 会生成此操作。在 Android 上,当用户将无障碍焦点放在组件上并按下音量减少按钮时,TalkBack 会生成此操作。'longpress'- 仅限 Android - 当用户将无障碍焦点放在组件上,然后双击并按住一根手指在屏幕上时生成此操作。无论是否有辅助技术,这都应该执行相同的操作。'expand'- 仅限 Android - 此操作“展开”组件,以便 TalkBack 宣布“已展开”提示。'collapse'- 仅限 Android - 此操作“折叠”组件,以便 TalkBack 宣布“已折叠”提示。
对于标准操作, label 字段是可选的,辅助技术通常不使用它。对于自定义操作,它是一个本地化字符串,包含要向用户展示的操作描述。
要处理操作请求,组件必须实现一个 onAccessibilityAction 函数。此函数的唯一参数是一个包含要执行的操作名称的事件。下面来自 RNTester 的示例展示了如何创建定义和处理几个自定义操作的组件。
<View
accessible={true}
accessibilityActions={[
{name: 'cut', label: 'cut'},
{name: 'copy', label: 'copy'},
{name: 'paste', label: 'paste'},
]}
onAccessibilityAction={event => {
switch (event.nativeEvent.actionName) {
case 'cut':
Alert.alert('Alert', 'cut action success');
break;
case 'copy':
Alert.alert('Alert', 'copy action success');
break;
case 'paste':
Alert.alert('Alert', 'paste action success');
break;
}
}}
/>
检查是否启用了屏幕阅读器
AccessibilityInfo API 允许你确定屏幕阅读器当前是否处于活动状态。请参阅 AccessibilityInfo 文档 以获取详细信息。
发送无障碍事件 Android
有时触发 UI 组件上的无障碍事件很有用(即当自定义视图出现在屏幕上或将无障碍焦点设置到视图时)。原生 UIManager 模块为此公开了一个方法 'sendAccessibilityEvent'。它接受两个参数:一个视图标签和一个事件类型。支持的事件类型是 typeWindowStateChanged、 typeViewFocused 和 typeViewClicked。
import {Platform, UIManager, findNodeHandle} from 'react-native';
if (Platform.OS === 'android') {
UIManager.sendAccessibilityEvent(
findNodeHandle(this),
UIManager.AccessibilityEventTypes.typeViewFocused,
);
}
测试 TalkBack 支持 Android
要启用 TalkBack,请转到 Android 设备或模拟器上的设置应用。点击无障碍,然后点击 TalkBack。切换“使用服务”开关以启用或禁用它。
Android 模拟器默认没有安装 TalkBack。你可以通过 Google Play 商店在模拟器上安装 TalkBack。确保选择一个安装了 Google Play 商店的模拟器。这些可在 Android Studio 中找到。
你可以使用音量键快捷方式来切换 TalkBack。要开启音量键快捷方式,请转到设置应用,然后无障碍。在顶部,开启音量键快捷方式。
要使用音量键快捷方式,同时按下两个音量键 3 秒钟以启动无障碍工具。
此外,如果你愿意,可以通过命令行使用以下命令切换 TalkBack:
# 禁用
adb shell settings put secure enabled_accessibility_services com.android.talkback/com.google.android.marvin.talkback.TalkBackService
# 启用
adb shell settings put secure enabled_accessibility_services com.google.android.marvin.talkback/com.google.android.marvin.talkback.TalkBackService
测试 VoiceOver 支持 iOS
要在 iOS 或 iPadOS 设备上启用 VoiceOver,请转到设置应用,点击通用,然后无障碍。在那里你会发现许多工具可供人们启用他们的设备以便更易用,包括 VoiceOver。要启用 VoiceOver,点击“视觉”下的 VoiceOver,并切换顶部出现的开关。
在无障碍设置的最底部,有一个“无障碍快捷方式”。你可以使用它通过三击主屏幕按钮来切换 VoiceOver。
模拟器上没有 VoiceOver,但你可以使用 Xcode 的 Accessibility Inspector 通过应用程序使用 macOS VoiceOver。注意,最好始终使用设备进行测试,因为 macOS 的 VoiceOver 可能会导致体验不同。