前言

说实话,我之前写代码从来不考虑无障碍——觉得那是"加分项",不是必选项。直到有天看到一个视障朋友用读屏软件操作手机,那个体验让我破防了。一个没有语义化标签的按钮,读屏软件只能读出"未命名按钮"——你让人家怎么用?
无障碍不是锦上添花,是基本的尊重。

A hand-drawn doodle illustration on pure white pap

好消息是,HarmonyOS7 的无障碍适配门槛很低,5 个改动就能覆盖大部分场景。今天逐个讲清楚。

无障碍(Accessibility)的目标很简单:让所有人都能用你的 App。不只是视障用户,还有老年人、手部不便的用户、甚至在强光下看不清屏幕的普通人。

你以为无障碍是少数人的需求?全球有超过 10 亿残障人士,中国超过 8500 万。而且很多应用商店(包括 AppGallery)已经开始把无障碍作为审核指标了。

不做无障碍,迟早要补课。

为什么要做无障碍

不只是道德层面,还有实际利益:

  • 用户覆盖 —— 不做无障碍 = 直接放弃大量潜在用户
  • 应用审核 —— 部分应用商店已把无障碍纳入评分
  • 代码质量 —— 语义化标签让代码更清晰,方便维护
  • SEO 类比 —— 就像网页做 SEO 一样,做好无障碍 = 做好"应用优化"

无障碍能力概览

HarmonyOS7 提供的无障碍能力一览:

能力 属性/方法 说明

A hand-drawn doodle sketchnote on pure white paper

| 语义化文本 | accessibilityText | 给组件添加读屏文本 |
| 语义化提示 | accessibilityHint | 提示操作结果,如"点击后打开设置" |
| 焦点管理 | accessibilityNextFocusId | 控制焦点移动顺序 |
| 组件角色 | accessibilityRole | 标记组件类型(按钮/链接/标题等) |
| 朗读支持 | accessibilityLevel | 控制组件是否可被朗读 |
| 触摸目标 | hitTestBehavior / responseRegion | 扩大可点击区域 |
| 高对比度 | colorMode | 适配高对比度模式 |
| 分组 | accessibilityGroup | 将多个组件作为整体朗读 |

改动 1:语义化标签(accessibilityText)

这是最重要、最优先要做的一件事。

没加 accessibilityText 的图标按钮,读屏软件只能读"按钮"——用户根本不知道这个按钮是干嘛的。

@Entry
@Component
struct AccessibilityTextDemo {
  build() {
    Row({ space: 16 }) {
      Image($r('app.media.icon_search'))
        .width(24)
        .height(24)
        .fillColor('#333333')
        .accessibilityText('搜索')
        .accessibilityHint('点击打开搜索页面')

      Image($r('app.media.icon_settings'))
        .width(24)
        .height(24)
        .fillColor('#333333')
        .accessibilityText('设置')
        .accessibilityHint('点击打开设置页面')

      Image($r('app.media.icon_notification'))
        .width(24)
        .height(24)
        .fillColor('#333333')
        .accessibilityText('通知')
        .accessibilityHint('点击查看通知消息')
    }
    .padding(16)
  }
}

A hand-drawn doodle illustration on pure white pap

关键代码讲解:

  • accessibilityText('搜索') —— 读屏时会朗读"搜索",而不是"图片"或"未命名"
  • accessibilityHint('点击打开搜索页面') —— 提示操作结果,用户知道点了会发生什么
  • 所有纯图标按钮必须加,这是最低要求

原则:如果一个组件去掉视觉样式后你不知道它是啥,就必须加 accessibilityText

改动 2:焦点管理

默认的焦点移动顺序是按布局从上到下、从左到右。但有些场景需要自定义顺序——比如表单里"密码"输完后应该跳到"确认密码",而不是跑到旁边的广告位。

@Entry
@Component
struct FocusOrderDemo {
  build() {
    Column({ space: 12 }) {
      TextInput({ placeholder: '用户名' })
        .width('100%')
        .height(48)
        .id('input_username')
        .accessibilityNextFocusId('input_password')

      TextInput({ placeholder: '密码' })
        .width('100%')
        .height(48)
        .id('input_password')
        .type(InputType.Password)
        .accessibilityNextFocusId('input_confirm')

      TextInput({ placeholder: '确认密码' })
        .width('100%')
        .height(48)
        .id('input_confirm')
        .type(InputType.Password)
        .accessibilityNextFocusId('btn_register')

      Button('注册')
        .width('100%')
        .height(48)
        .id('btn_register')
    }
    .padding(24)
  }
}

关键代码讲解:

  • .id('input_username') —— 给组件设 ID,焦点管理要用
  • accessibilityNextFocusId('input_password') —— 指定下一个焦点跳转目标,从用户名直接跳到密码
  • 这样焦点顺序就是:用户名 → 密码 → 确认密码 → 注册按钮,不会跳到不该跳的地方

改动 3:朗读支持

有些装饰性元素(分割线、背景图、间距)不需要被朗读,有些关键信息又容易漏掉。用 accessibilityLevelaccessibilityGroup 来控制:

@Entry
@Component
struct ReadLevelDemo {
  build() {
    Scroll() {
      Column({ space: 16 }) {
        Text('今日推荐')
          .fontSize(24)
          .fontWeight(FontWeight.Bold)
          .accessibilityLevel('yes')
          .accessibilityRole('HEADER')

        Row() {
          Image($r('app.media.icon_decoration'))
            .width(40)
            .height(40)
            .accessibilityLevel('no')
        }

        Row() {
          Image($r('app.media.icon_avatar'))
            .width(36)
            .height(36)
            .accessibilityText('用户头像')

          Column() {
            Text('张三')
              .fontSize(16)
              .fontWeight(FontWeight.Bold)
            Text('3 分钟前发布')
              .fontSize(12)
              .fontColor('#999')
          }
          .margin({ left: 8 })
          .accessibilityGroup(true)
          .accessibilityText('张三,3分钟前发布')
        }
        .padding(12)
        .backgroundColor('#f5f5f5')
        .borderRadius(8)
      }
      .padding(16)
    }
  }
}

关键代码讲解:

  • accessibilityLevel('yes') —— 明确可被朗读(默认就是 yes,显式写出来更清晰)
  • accessibilityLevel('no') —— 不可朗读,装饰性图片用这个,读屏会跳过
  • accessibilityGroup(true) —— 把子组件合并为整体朗读,不会分别读"张三"和"3分钟前发布",而是一次性读"张三,3分钟前发布"
  • accessibilityRole('HEADER') —— 标记这是标题,读屏软件会额外提示"标题级别"

改动 4:高对比度适配

部分用户会开启系统的高对比度模式,如果你的 App 用了浅灰色文字(#999999)配白色背景,高对比度模式下可能直接看不见。

@Entry
@Component
struct HighContrastDemo {
  @StorageLink('isHighContrast') isHighContrast: boolean = false

  build() {
    Column({ space: 12 }) {
      Text('普通模式下的提示文字')
        .fontSize(14)
        .fontColor(this.isHighContrast ? '#000000' : '#999999')

      Divider()
        .color(this.isHighContrast ? '#000000' : '#e0e0e0')
        .strokeWidth(this.isHighContrast ? 2 : 1)

      Button('确认操作')
        .width('100%')
        .height(48)
        .backgroundColor('#007DFF')
        .fontColor(Color.White)
    }
    .padding(24)
  }
}

关键代码讲解:

  • @StorageLink('isHighContrast') —— 监听系统高对比度状态变化
  • fontColor(this.isHighContrast ? '#000000' : '#999999') —— 高对比度模式用纯黑,普通模式用灰色
  • 按钮颜色不用改,蓝底白字本身对比度就够

实际项目中,可以用资源限定词目录(resources/high_contrast/)自动适配,不用手动判断。

改动 5:触摸目标扩大

小图标是触屏的噩梦——看得见点不着。HarmonyOS7 规范建议触摸目标最小 44x44vp,但很多图标只有 24x24vp。用 responseRegion 扩大点击区域:

@Entry
@Component
struct TouchTargetDemo {
  build() {
    Row({ space: 24 }) {
      Image($r('app.media.icon_back'))
        .width(24)
        .height(24)
        .fillColor('#333333')
        .accessibilityText('返回')
        .responseRegion({ x: -10, y: -10, width: 44, height: 44 })
        .onClick(() => {
          console.info('返回被点击')
        })

      Image($r('app.media.icon_close'))
        .width(24)
        .height(24)
        .fillColor('#333333')
        .accessibilityText('关闭')
        .responseRegion({ x: -10, y: -10, width: 44, height: 44 })
        .onClick(() => {
          console.info('关闭被点击')
        })
    }
    .padding(16)
  }
}

关键代码讲解:

  • responseRegion({ x: -10, y: -10, width: 44, height: 44 }) —— 扩大可点击区域到 44x44vp
  • x: -10, y: -10 —— 向左上各扩展 10vp,因为图标本身是 24x24,扩展后刚好 44x44
  • 视觉大小不变,但点击范围变大了——用户感知不到区别,但操作准确率大幅提升

测试验证方法

代码写完了,怎么验证无障碍是否生效?

  1. 开启读屏模式:设置 → 辅助功能 → 屏幕朗读,开启后触摸屏幕即可听到朗读内容
  2. 焦点导航:用音量键上下切换焦点,检查焦点顺序是否正确
  3. Accessibility Inspector:DevEco Studio 自带的检查工具,可以查看组件的无障碍属性
  4. 高对比度测试:设置 → 辅助功能 → 高对比度文字,开启后检查 UI 是否正常

快速自测清单:

  • 所有图标按钮有 accessibilityText
  • 装饰性元素有 accessibilityLevel('no')
  • 表单焦点顺序正确
  • 触摸目标 ≥ 44x44vp
  • 高对比度模式下文字可读

写在最后

无障碍适配的 5 个改动,按优先级排:

  1. accessibilityText —— 最重要,没这个读屏就是瞎子
  2. 触摸目标扩大 —— 影响所有用户,不只是残障用户
  3. 焦点管理 —— 表单场景必做
  4. 朗读控制 —— 避免信息过载或遗漏
  5. 高对比度适配 —— 锦上添花但体现专业度

这 5 个改动加起来可能也就半天工作量,但对用户的体验提升是质的飞跃。别等应用商店强制要求了再补,现在就做。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐