Expo应用的可访问性(Accessibility)是确保所有用户,包括有视觉、听觉、运动或认知障碍的用户,都能有效使用应用的重要方面。Expo和React Native提供了丰富的可访问性API和属性。
可访问性基础:
- accessibilityLabel
为屏幕阅读器提供元素的描述。
typescript<Image source={{ uri: 'https://example.com/image.jpg' }} accessibilityLabel="用户头像" style={{ width: 50, height: 50 }} /> <Button title="提交" accessibilityLabel="提交表单" onPress={handleSubmit} />
- accessibilityHint
提供有关元素行为的额外信息。
typescript<TouchableOpacity accessibilityLabel="查看详情" accessibilityHint="点击查看用户详细信息" onPress={handlePress} > <Text>查看</Text> </TouchableOpacity>
- accessibilityRole
指定元素的UI角色。
typescript<View accessibilityRole="button" accessibilityLabel="确认" onClick={handleConfirm} > <Text>确认</Text> </View>
常用可访问性角色:
button:按钮link:链接header:标题text:文本image:图片search:搜索框adjustable:可调节控件
- accessibilityState
描述元素的当前状态。
typescript<CheckBox accessibilityLabel="同意条款" accessibilityState={{ checked: isChecked, disabled: false, }} value={isChecked} onValueChange={setIsChecked} />
可访问性状态:
disabled:禁用状态selected:选中状态checked:勾选状态busy:忙碌状态expanded:展开状态
- accessibilityValue
描述元素的值。
typescript<Slider accessibilityLabel="音量" accessibilityValue={{ min: 0, max: 100, now: volume }} value={volume} onValueChange={setVolume} /> <ProgressBar accessibilityLabel="下载进度" accessibilityValue={{ min: 0, max: 100, now: progress }} progress={progress / 100} />
可访问性操作:
- accessibilityActions
定义元素支持的可访问性操作。
typescript<View accessibilityLabel="播放控制" accessibilityActions={[ { name: 'increment', label: '增加音量' }, { name: 'decrement', label: '减少音量' }, { name: 'magicTap', label: '双击播放/暂停' }, ]} onAccessibilityAction={(event) => { switch (event.nativeEvent.actionName) { case 'increment': setVolume((v) => Math.min(v + 10, 100)); break; case 'decrement': setVolume((v) => Math.max(v - 10, 0)); break; case 'magicTap': togglePlayPause(); break; } }} > <Text>音量: {volume}</Text> </View>
- onAccessibilityEscape
定义转义操作。
typescript<Modal visible={isVisible} onAccessibilityEscape={() => setIsVisible(false)} accessibilityViewIsModal={true} > <View> <Text>模态框内容</Text> <Button title="关闭" onPress={() => setIsVisible(false)} /> </View> </Modal>
可访问性属性:
- accessible
标记元素为可访问的。
typescript<View accessible={true}> <Text>这个视图可以被屏幕阅读器访问</Text> </View>
- accessibilityElementsHidden
隐藏子元素的可访问性。
typescript<View accessible={true} accessibilityLabel="容器" accessibilityElementsHidden={isHidden} > <Text>子元素1</Text> <Text>子元素2</Text> </View>
- accessibilityIgnoresInvertColors
忽略颜色反转设置。
typescript<Image source={{ uri: 'https://example.com/chart.jpg' }} accessibilityIgnoresInvertColors={true} style={{ width: 200, height: 200 }} />
焦点管理:
- focusable
使元素可聚焦。
typescript<TextInput focusable={true} accessibilityLabel="用户名输入框" placeholder="请输入用户名" />
- accessibilityLiveRegion
标记动态内容区域。
typescript<Text accessibilityLiveRegion="polite" accessibilityLabel="状态信息" > {statusMessage} </Text>
可访问性事件:
typescriptfunction useAccessibilityFocus() { const [isFocused, setIsFocused] = useState(false); const handleFocus = () => { setIsFocused(true); console.log('Element focused'); }; const handleBlur = () => { setIsFocused(false); console.log('Element blurred'); }; return { isFocused, handleFocus, handleBlur }; }
语义化组件:
- 使用语义化HTML标签(Web)
typescript// 在Web平台上使用语义化标签 if (Platform.OS === 'web') { return ( <nav accessibilityRole="navigation"> <ul> <li><a href="/home">首页</a></li> <li><a href="/about">关于</a></li> </ul> </nav> ); }
- 使用正确的可访问性角色
typescript// 按钮使用button角色 <TouchableOpacity accessibilityRole="button" accessibilityLabel="提交" onPress={handleSubmit} > <Text>提交</Text> </TouchableOpacity> // 链接使用link角色 <TouchableOpacity accessibilityRole="link" accessibilityLabel="查看详情" onPress={handlePress} > <Text>查看详情</Text> </TouchableOpacity>
最佳实践:
- 提供清晰的标签
typescript// 好的实践 <Button title="提交表单" accessibilityLabel="提交用户注册表单" onPress={handleSubmit} /> // 避免重复 <Button title="提交" accessibilityLabel="提交" // 与title重复 onPress={handleSubmit} />
- 使用有意义的提示
typescript<TouchableOpacity accessibilityLabel="删除项目" accessibilityHint="此操作无法撤销,请谨慎操作" onPress={handleDelete} > <Text>删除</Text> </TouchableOpacity>
- 支持键盘导航
typescriptfunction KeyboardNavigation() { const [focusedIndex, setFocusedIndex] = useState(0); const handleKeyDown = (event) => { if (event.key === 'ArrowDown') { setFocusedIndex((i) => Math.min(i + 1, items.length - 1)); } else if (event.key === 'ArrowUp') { setFocusedIndex((i) => Math.max(i - 1, 0)); } else if (event.key === 'Enter') { items[focusedIndex].onPress(); } }; return ( <View onKeyDown={handleKeyDown}> {items.map((item, index) => ( <TouchableOpacity key={index} accessibilityLabel={item.label} focusable={true} style={[ styles.item, focusedIndex === index && styles.focused, ]} onPress={item.onPress} > <Text>{item.label}</Text> </TouchableOpacity> ))} </View> ); }
- 支持屏幕阅读器
typescriptfunction ScreenReaderSupport() { const isScreenReaderEnabled = useAccessibilityInfo(); return ( <View> {isScreenReaderEnabled ? ( <Text>屏幕阅读器已启用</Text> ) : ( <Text>屏幕阅读器未启用</Text> )} </View> ); }
- 测试可访问性
typescriptimport { AccessibilityInfo } from 'react-native'; async function testAccessibility() { // 检查屏幕阅读器是否启用 const isScreenReaderEnabled = await AccessibilityInfo.isScreenReaderEnabled(); console.log('Screen reader enabled:', isScreenReaderEnabled); // 检查减少动画设置 const isReduceMotionEnabled = await AccessibilityInfo.isReduceMotionEnabled(); console.log('Reduce motion enabled:', isReduceMotionEnabled); // 监听可访问性变化 AccessibilityInfo.addEventListener( 'screenReaderChanged', (isEnabled) => { console.log('Screen reader changed:', isEnabled); } ); }
可访问性工具:
- AccessibilityInfo API
typescriptimport { AccessibilityInfo } from 'react-native'; // 获取可访问性信息 const isScreenReaderEnabled = await AccessibilityInfo.isScreenReaderEnabled(); const isReduceMotionEnabled = await AccessibilityInfo.isReduceMotionEnabled(); // 监听变化 AccessibilityInfo.addEventListener('screenReaderChanged', (isEnabled) => { console.log('Screen reader:', isEnabled); }); AccessibilityInfo.addEventListener('reduceMotionChanged', (isEnabled) => { console.log('Reduce motion:', isEnabled); });
- 可访问性检查工具
- iOS:VoiceOver
- Android:TalkBack
- Web:屏幕阅读器(NVDA、JAWS等)
常见可访问性问题:
-
缺少可访问性标签
- 为所有交互元素添加accessibilityLabel
- 为图片提供描述性标签
-
焦点管理不当
- 确保键盘导航顺序合理
- 提供清晰的焦点指示器
-
颜色对比度不足
- 确保文本和背景有足够的对比度
- 支持高对比度模式
-
动态内容未通知
- 使用accessibilityLiveRegion标记动态内容
- 及时通知屏幕阅读器内容变化
通过实施这些可访问性实践,可以确保Expo应用对所有用户都是友好和可用的。