ArkUI + HarmonyOS 取件码卡片:大字体排版与无障碍语义【共创稿事节】
一张物流取件码卡片,看上去比聊天页面简单:驿站名称、六位取件码、截止时间,再加一个“已取件”按钮。但如果把字号扩大到普通模式的 1.4 倍,最先出问题的往往不是字号,而是布局假设。原本一行放得下的驿站地址被截断,取件码和操作按钮挤在一起;屏幕朗读选中卡片时,又可能把“682419”读成一串难以分组的数字。对于站在快递柜前、单手拿着包裹的使用者,这些细小问题会直接拖慢完成任务的速度。
这篇采用一款独立的 ParcelEase 取件卡片作为案例,尝试把“看得清、点得到、听得懂”转换成可实现的 ArkUI 组件约束。文章里的取件码 682-419 是界面样例,不关联任何真实快递订单;正式项目应从受控业务接口取得状态,并避免在锁屏预览、分析日志里暴露完整取件凭证。我们只围绕大字体排版、语义标签、状态反馈三个 UI 问题展开,不引入收件人信息、真实物流 SDK 或未经核实的平台能力。

一、把“易读”拆成视觉与交互约束
取件码页面有四个相互影响的区块:驿站位置、凭证号码、取件提示和完成操作。常规手机的可用宽度并不稳定,字体缩放、分屏、折叠态、系统安全区都会改变可用空间。所以不应把卡片写成固定高度,然后尝试在文本塞不进去时盲目缩小字体。长地址应当允许自然换行;号码必须留出完整显示空间;主操作按钮应保持足够高的触控面积;内容超出时让外层 Scroll 接管,而不是让卡片内容溢出屏幕。
为了让行为可复现,页面提供两个显示级别:“标准字体”和“放大字体”,用 @State 控制 scale 为 1.0 或 1.4。它是应用内部的示例设置,不声称自动读取了系统无障碍的文字缩放系数。工程里如果要融合系统字体设置,应另行遵守对应的应用配置与资源缩放策略,并测试系统字体缩放与应用内放大叠加的效果。更关键的是,不论哪种模式,逻辑状态永远只保留一个 pickedUp 布尔值,不能让两个视觉分支各自维护一份“已完成”副本。
读者可以先为样例卡片建立简单的数据模型。把展示数据从组件分离,后面才有条件检测长地址、空驿站名称以及取件码格式。真实项目的取件码不一定永远是数字,可能含有字母、分隔符,也可能由站点临时刷新。因此这里的数据检查只适合本篇明确的六位数字测试场景,不要把它写成物流行业的统一协议。
// entry/src/main/ets/model/ParcelCard.ets
export interface ParcelCard {
station: string;
place: string;
code: string;
expires: string;
}
export const SAMPLE: ParcelCard = {
station: '云栖驿站',
place: '北门综合服务点二层,靠近自助寄件区',
code: '682419',
expires: '请在三日内取件'
};
export function spacedCode(value: string): string {
if (!/^\d{6}$/.test(value)) { return '凭证待核对'; }
return value.substring(0, 3) + '-' + value.substring(3);
}
export function speakCode(value: string): string {
const digits: string[] = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
if (!/^\d{6}$/.test(value)) { return '取件码待核对'; }
const items: string[] = value.split('').map((c: string) =>
digits[Number(c)]);
return '取件码,' + items.slice(0, 3).join(',') +
',' + items.slice(3).join(',');
}
spacedCode() 只是视觉排版,把六位数拆成三位一组;speakCode() 则负责给屏幕朗读生成更清楚的中文序列。它们消费同一条数据,却承担不同任务。不要把朗读文本直接写进数字视图,否则视觉卡片会变成冗长的“六、八、二……”字串;也不能在无障碍标签里使用未校验的原始字符串,避免不完整数据被误读成有效凭证。示例数字在文章的代码、画面和验证步骤里保持一致。
二、用 Scroll 和弹性卡片替代固定高度
取件码的视觉层不是“按钮 + 几段文字”的机械堆放。首先是层级关系:页面标题介绍用途;驿站名称和位置提供场景;取件码最醒目;操作区说明当前状态。其次是弹性规则:地址允许两行以上换行,放大字号时高度自然增长;号码区域可独立换行,不与按钮共享固定一行;底部操作按钮最小高度单独控制。以下片段展示 ParcelPage.ets 的主要实现,采用 ArkUI V1 的 @Entry 和 @Component。
// entry/src/main/ets/pages/ParcelPage.ets
import { SAMPLE, spacedCode, speakCode } from '../model/ParcelCard';
@Entry
@Component
struct ParcelPage {
@State private scale: number = 1.4;
@State private pickedUp: boolean = false;
build() {
Scroll() {
Column({ space: 18 }) {
Text('ParcelEase').fontSize(20).fontColor('#2764B9')
Text('物流取件卡片')
.fontSize(26 * this.scale).fontWeight(FontWeight.Bold)
Row({ space: 12 }) {
Button('标准字体').onClick(() => { this.scale = 1.0; })
.backgroundColor(this.scale === 1.0 ? '#2568BA' : '#D8E5F8')
Button('放大字体').onClick(() => { this.scale = 1.4; })
.backgroundColor(this.scale === 1.4 ? '#2568BA' : '#D8E5F8')
}
Column({ space: 14 }) {
Text(SAMPLE.station)
.fontSize(21 * this.scale).fontWeight(FontWeight.Bold)
Text(SAMPLE.place).fontSize(16 * this.scale)
.fontColor('#4A5568').width('100%')
Text('取件码').fontSize(16 * this.scale)
Text(spacedCode(SAMPLE.code))
.fontSize(38 * this.scale)
.fontWeight(FontWeight.Bold)
.fontColor('#184E97')
.accessibilityText(speakCode(SAMPLE.code))
Text(SAMPLE.expires).fontSize(15 * this.scale)
Button(this.pickedUp ? '已完成取件' : '确认已取件')
.height(58).width('100%')
.enabled(!this.pickedUp)
.accessibilityText(this.pickedUp ? '已完成取件' : '确认已取件')
.onClick(() => { this.pickedUp = true; })
}.padding(24).width('100%')
.backgroundColor('#FFFFFF').borderRadius(24)
}.padding(20).width('100%')
}.height('100%').backgroundColor('#EFF5FC')
}
}
示例里字号放大采用数字倍率,目的是示例布局从 1.0 到 1.4 的差异,实际工程还应统一字号令牌与间距令牌;不要把每个组件都写死成互不相关的数字。按钮的高度和左右内边距需要在真实设备上检验,不能仅看设计稿。取件码放大后若当前宽度不足,可以降低号码本身的字号上限或采用每组三位的换行布局,但应优先保证全部数字可见,不要用省略号把凭证最后一位藏起来。

三、无障碍标签不是把屏幕文字重复一遍
ArkUI 公开的通用属性 accessibilityText() 可以为组件指定屏幕朗读时使用的文本。文本组件已经有可见字符,但“682-419”在不同读屏设置下的节奏难以保证,因此为号码设置明确的中文数字序列;按钮则播报它当前的业务动作。需要注意:当组件既有可见文字又设置了无障碍文本,朗读会优先使用配置的无障碍文本。过分依赖统一的“点击这里”“查看详情”一类标签,会让多个元素在朗读时失去区分度。
另一方面,页面会出现“已完成取件”状态,它在视觉上应通过文字和状态色一起反馈。不要只把按钮从蓝色变成灰色,却不调整标签:颜色障碍用户和屏幕朗读用户可能无法知道操作已经发生。示例在点击后修改 pickedUp,按钮的文字同时变化、按钮禁用防止重复提交,朗读标签也跟着更新。但这里的点击只切换页面状态,真实物流业务还要等待后台确认才能展示最终完成。若接口返回失败,应该将操作恢复为可尝试状态并说明原因,而不是因为 UI 已经变灰就判定成功。
对于视觉层,关键不是追求单一“无障碍主题色”,而是保证文本和背景之间的对比、反馈文字可读、焦点经过内容时顺序合理。真正的屏幕朗读测试需要在设备中启用对应辅助服务,依次滑动选中页面标题、驿站名称、地址、号码、确认按钮,确认朗读内容不冗余、不丢失,并且不会重复播报卡片中的所有信息。本文的页面示意仅展示布局,不能代替语音输出或辅助服务的实际验证。
四、把不便于截图发现的问题列入测试
常规截图能发现文字溢出,却未必能发现焦点不可达、朗读顺序错误和按钮重复触发。本篇建议先按两组显示级别建立矩阵:在标准字号下,检查长地址自然换行、取件码和按钮均完整;在 1.4 倍字号下,检查取件码最后一位仍可见,页面可以垂直滚动,按钮不会被页面底部安全区挡住。随后把窗口从较窄宽度切换到较宽宽度,确保逻辑状态不因布局重排而复位。这里不依赖某个神奇的适配 API,而是使用 ArkUI 原有容器的自然布局能力。
数据边界也要有明确结果。spacedCode('682419') 应返回 682-419,spacedCode('68A419') 则返回“凭证待核对”;speakCode('682419') 要依次读出六、八、二、四、一、九。页面拿到非法凭证时,不能仍展示一个巨大的确认按钮并假装凭证可用。示例代码采用固定的有效数据,接入网络数据后应先调用校验函数,并把 valid 作为按钮是否可用的条件。对于超长驿站名,可把标题容纳为两行并允许整体卡片增长;对于没有地址的条目,应显示“位置待更新”而不是直接展示空白卡片。
再看一个真实容易出现的交互边界:用户触摸“确认已取件”之后页面进入完成态,而外部接口最终返回超时。工程中应该至少区分 idle、submitting、done、failed 四个状态,再让按钮分别显示“确认已取件”“提交中”“已完成取件”“重试提交”。当前独立案例不包含真实接口,故只示例本地一次性确认。若项目后续扩展为扫码取件,扫码结果还应匹配当前站点和订单状态;单凭六位字符串的格式检查不能验证取件资格。

工程复盘时还应关注文本国际化。同样的中文地址若被翻译成较长的语言,字形宽度与换行位置可能明显不同;英文数字混排时,连字符也可能影响朗读节奏。不要把所有提示内容拼成一张图,因为图片在不同字号和屏幕朗读下缺乏天然的语义信息。优先用 Text、Button 等原生组件表达关键业务信息,图片只负责辅助识别。最后,在设备中测试屏幕朗读、字体大小、不同窗口宽度与触控操作,而不是把预览器的一张正常截图当作全部验收结论。
五、适用边界与工程取舍
ParcelEase 的重点是把一张看似普通的卡片当成完整的小型交互:展示数据拥有确定结构,字号有可以测试的倍率,布局允许内容增长,朗读拥有与视觉不同但一致的语义,操作状态可以被使用者明确感知。这样做不会自动让 App 满足所有无障碍规范,但比只在最后给按钮补一个标签更容易扩展和检查。
真正上线时,还要补齐服务端状态确认、短时凭证保护、过期处理、加载失败重试、设备辅助服务实测和不同语言适配。尤其是含有取件凭证的页面,应考虑展示范围与遮挡策略,避免凭证随便进入系统截图、分析日志或非必要的通知内容。本文选择可在 ArkUI 公开组件内实现的规则作为起点,保持边界清晰:页面能读、能点,并不代表后端业务已经授权完成;UI 的无障碍语义也不能替代业务数据的安全校验。
更多推荐

所有评论(0)