HarmonyOS 7 闪控窗排队服务无障碍整合:连载08(共08篇)【共创稿事节】

活动入场页面的核心信息并不复杂,但“排队中”“第28号”“预计等待12分钟”经常分布在不同字号、颜色与图标之间。对使用屏幕朗读、外接键盘或放大文字的用户来说,信息是否可以按顺序理解,比页面有没有醒目的渐变色更重要。尤其是闪控球和闪控窗只承担轻量任务,过度压缩信息可能让用户不知道当前是否应该前往入口。

本系列已经围绕城市艺术夜展构建了排队状态机、凭证关联、轻量窗口和统一状态协调器。最后一篇把这些能力连接到易读的界面层:业务快照不变,展示文案、可达动作和异常回退随界面形态调整。这样既保持轻量窗口的信息密度,又能让用户通过主应用完成全部操作。

排队轻量窗口的无障碍布局设计

一、先定义不依赖颜色的状态语言

许多排队卡片用蓝色表示等待、橙色表示叫号、灰色表示结束。如果没有对应的文字,屏幕朗读得到的内容可能只剩下数字,用户难以分辨接下来该做什么。业务模型里已经有 EntryPhase,现在为每个阶段建立一条可以直接显示和朗读的状态语句,并把需要关注的动作从进度条中单独提取出来。

阶段主要文案可执行动作展示重点
已预约凭证已领取,等待活动开始查看活动信息时间与场馆
排队中当前第28号,预计等待12分钟刷新、查看凭证序号与更新时间
请前往入口已叫号,请准备入场打开凭证入口与凭证
已入场入场登记完成返回活动详情完成状态
已取消或失效当前凭证不可继续入场查看处理说明原因与后续路径
需要核对最近进度仍可查看,等待更新重新获取状态保持最后有效信息

从视觉呈现到朗读文案应共享同一套语义投影。显示上可以把“28”做大,但辅助文案仍应包含“当前第28号”,不能让朗读服务猜测数字代表的业务字段。网络失去连接或出现事件冲突时,提示也必须是文字,不仅依赖红色感叹号。

二、从队列快照生成可访问的内容

这里不修改 QueueStore 内的事实状态,而是增加 QueueA11yProjection。它根据业务阶段选择合适的文本,必要时附上异常提示。业务投影可以被主页面、闪控窗内容页和外接键鼠模式共同复用,避免三个入口使用互不一致的提示词。

// logic/QueueA11yProjection.ets
import { EntryPhase, QueueSnapshot } from '../model/QueueModel';

export interface QueueA11yText {
  headline: string;
  detail: string;
  readout: string;
  actionLabel: string;
}

export function projectQueueA11y(s: QueueSnapshot,
  needsReview: boolean): QueueA11yText {
  if (needsReview) {
    return {
      headline: '状态需要核对',
      detail: '保留最近一次有效的入场进度',
      readout: '排队状态需要重新获取,请查看最近一次有效信息',
      actionLabel: '重新获取状态'
    };
  }
  switch (s.phase) {
    case EntryPhase.WAITING:
      return { headline: '排队中',
        detail: `当前第${s.position}号,预计等待${s.waitMinutes}分钟`,
        readout: `排队中,当前第${s.position}号,预计等待${s.waitMinutes}分钟`,
        actionLabel: '查看入场凭证' };
    case EntryPhase.CALLED:
      return { headline: '请前往入口', detail: '请准备入场凭证',
        readout: '已经叫号,请前往入口并准备入场凭证',
        actionLabel: '查看入场凭证' };
    case EntryPhase.ADMITTED:
      return { headline: '已入场', detail: '入场登记完成',
        readout: '入场登记完成', actionLabel: '返回活动详情' };
    case EntryPhase.RESERVED:
      return { headline: '已预约', detail: '请关注活动开始时间',
        readout: '已预约,请关注活动开始时间', actionLabel: '查看活动信息' };
    default:
      return { headline: '凭证状态已结束', detail: '查看凭证处理说明',
        readout: '当前凭证不可继续入场,请查看处理说明',
        actionLabel: '查看处理说明' };
  }
}

needsReview 只是上篇一致性协调器暴露的页面提示,不改变 phase。当服务端确认最新状态时,投影重新计算即可。对于扫码凭证、位置说明等可能包含隐私的信息,朗读内容只提供必要语义,不直接读取完整票号。等待时间属于估计值,因此界面要明确写“预计”,并展示更新时间,不能把它描述为精确到分钟的承诺。

三、让摘要与操作分别成为可访问节点

ArkUI 支持通过 accessibilityText 为组件补充朗读内容,通过 accessibilityGroup 合并一组信息。摘要可以组成一个完整朗读单元,而“查看凭证”“刷新状态”等按钮仍需独立获焦。若把整张卡片连同按钮一起分组,辅助功能就可能无法逐个定位操作入口。外接键盘场景下,按钮通常具备默认获焦能力,页面仍应检查焦点顺序是否符合阅读顺序。

// view/QueueAccessibleSummary.ets
@Component
export struct QueueAccessibleSummary {
  @Prop eventTitle: string = '';
  @Prop headline: string = '';
  @Prop detail: string = '';
  @Prop readout: string = '';

  build() {
    Column({ space: 8 }) {
      Text(this.eventTitle)
        .fontSize(20).fontWeight(FontWeight.Bold)
      Text(this.headline)
        .fontSize(22).fontWeight(FontWeight.Bold)
        .fontColor('#174E9E')
      Text(this.detail).fontSize(18).fontColor('#29384F')
    }
    .width('100%')
    .padding(16)
    .backgroundColor('#EDF5FF')
    .borderRadius(16)
    .accessibilityGroup(true)
    .accessibilityText(`${this.eventTitle},${this.readout}`)
  }
}

屏幕朗读在处理复合节点时,应按“活动名称—当前阶段—数字含义—建议动作”的顺序组织文本。组件结构可以有独立的装饰性线条和图标,但它们无需重复朗读;如果图标承担“展开”“关闭”等实际操作,就要提供准确名称。为了防止重复播报,同一个状态不应在标题、摘要和通知区连续复制多遍。

ArkUI 无障碍文本与按钮焦点布局

四、在主页面保留两个稳定的操作出口

轻量窗口显示的信息有限,但主页面仍需让用户完成刷新与凭证查看。下面页面直接消费上一节的投影组件,沿用 QueueStore 的订阅方式,保持单一业务状态来源。入口动作与数据服务接线由具体应用的仓储和导航层负责,页面不自行改写入场阶段。

// pages/QueueAccessiblePage.ets
import { QueueSnapshot, initialSnapshot } from '../model/QueueModel';
import { queueStore } from '../logic/QueueStore';
import { queueCoordinator } from '../logic/QueueConsistencyCoordinator';
import { projectQueueA11y, QueueA11yText } from '../logic/QueueA11yProjection';
import { QueueAccessibleSummary } from '../view/QueueAccessibleSummary';

@Entry
@Component
struct QueueAccessiblePage {
  @State snapshot: QueueSnapshot = initialSnapshot();
  @State needsReview: boolean = false;
  @State showPass: boolean = false;
  private dispose?: () => void;

  aboutToAppear(): void {
    this.dispose = queueStore.listen('entry-7264',
      (value: QueueSnapshot): void => { this.snapshot = value; });
  }

  aboutToDisappear(): void {
    this.dispose?.();
    this.dispose = undefined;
  }

  private refreshLocalView(): void {
    const latest: QueueSnapshot | undefined =
      queueStore.snapshot(this.snapshot.ticketId);
    if (latest) this.snapshot = latest;
    this.needsReview = queueCoordinator.notice(this.snapshot.ticketId).length > 0;
  }

  private openPass(): void {
    this.showPass = true;
  }

  build() {
    Column({ space: 16 }) {
      QueueAccessibleSummary({
        eventTitle: '城市艺术夜展',
        headline: projectQueueA11y(this.snapshot, this.needsReview).headline,
        detail: projectQueueA11y(this.snapshot, this.needsReview).detail,
        readout: projectQueueA11y(this.snapshot, this.needsReview).readout
      })
      if (this.needsReview) {
        Text('进度需要重新获取,最近一次有效信息仍可查看')
          .fontSize(16).fontColor('#9A4B0A')
      }
      Button('查看入场凭证')
        .width('100%').height(48)
        .accessibilityText('查看城市艺术夜展的入场凭证')
        .focusable(true)
        .onClick(() => this.openPass())
      Button('刷新本地进度')
        .width('100%').height(48)
        .accessibilityText('读取当前应用已确认的排队状态')
        .focusable(true)
        .onClick(() => this.refreshLocalView())
      if (this.showPass) {
        Text('城市艺术夜展 · 入场凭证')
          .fontSize(18).fontWeight(FontWeight.Bold)
        Text('凭证尾号 7264,请按活动规则出示有效凭证')
          .fontSize(16)
      }
    }
    .width('100%').padding(20)
  }
}

这里重点展示可访问界面与业务协作的边界。refreshLocalView() 只读取 QueueStore 中已经确认的有效快照,适合作为窗口恢复后的本地刷新;需要从服务端拉取最新状态时,应继续使用工程现有的网络仓储,经 QueueConsistencyCoordinator 检查后再写入 QueueStore。openPass() 先展示当前凭证的脱敏摘要,项目集成时可替换为前面已建立的独立凭证页导航。两类动作都应该呈现错误反馈。对于连续刷新,还应设置请求进行中的禁用状态,减少并发读取造成的信息闪烁。

五、大字号、窄窗口和焦点顺序需要一起考虑

闪控窗的可用面积小,最容易出现三个问题:标题太长挤压数字、按钮缩窄到难以点击、状态文字被进度条遮挡。应优先保留活动名称和当前阶段,让次要说明换行或进入详情页;序号不应使用图片承载,才能随字体比例正常调整。缩放字号后检查卡片总高度、滚动区域与底部操作区,不应靠固定高度隐藏溢出内容。

外接键盘与鼠标场景需要明确的走焦顺序。建议从活动信息、状态摘要进入“查看凭证”,再到“重新获取状态”。使用默认可聚焦的 Button 通常比在每段文本上人为添加焦点更稳妥。卡片中若有可点击的进度节点,应补充节点含义、选择状态以及点击结果;纯展示性的步骤线不需要变成额外的操作目标。

排队状态与凭证入口的竖屏布局

六、用一张场景矩阵收尾整个工程

工程检查要覆盖网络、业务和界面三类变化,不能只关注正常的排队路径。应安排以下场景逐项观察可见文案与焦点:进入后台后返回、闪控窗关闭后再打开、同版本冲突、服务端返回凭证失效、用户快速连续刷新、活动名称极长、系统放大字体、只用键盘访问按钮,以及屏幕朗读从标题走到操作入口。

检查结果需要同时满足两点:第一,用户能读到当前有效状态和对应的下一步操作;第二,任何界面动作都不应绕过统一快照校验来改变入场资格。若主应用需要显示更完整的活动场馆与说明,应通过稳定的“查看凭证”或“查看活动”入口进入,不要求用户在狭小的轻量窗口里完成所有事情。

经过八篇的逐步建设,城市艺术夜展的业务链路已经从状态模型延伸到活动列表、闪控球、闪控窗、事件归约、窗口生命周期、冲突核对和无障碍呈现。能够长期复用的核心并非某一种窗口外观,而是“单一可信业务快照+分层投影+明确动作出口”的组合。后续增加其他预约活动时,可以沿用这套数据和交互规则,只替换具体活动内容与服务端协议。

相关文档:屏幕朗读适配指南、ArkUI 焦点控制、Accessibility Kit 概览。

Logo

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

更多推荐