同一条工单提示,在展开的大屏上读起来没什么压力,进入折叠屏窄栏后却容易变成一个不停横移的短条。文字并没有少,但用户要等到末尾才能读完“确认附件完整性”;如果此时切到别的工单,滚动内容又可能在视觉上和新的详情混在一起。这里的矛盾不在于让文字动起来有多难,而在于哪种状态应该允许文字继续动。这次用一个独立的业务演示 NoticeLane,把工单 NL-083 的通知 NT-204 固定为“资料等待业务复核,请确认附件完整性”。默认采用可换行的静态阅读模式,窄栏里只有用户主动关闭“阅读优先”时才会进入跑马灯状态。文章中的页面与日志均是预设演示数据,不等于已经在折叠屏上实测。## 一、先把视觉滚动与文本可达性分开在工单列表中,横向滚动通知有一个实际优点:不显著增加卡片高度,能够保住右侧时间和操作按钮。但进入详情以后,同样的通知成了处理前必须读清楚的依据。把列表场景的单行规则直接搬进详情,是信息层级错误,不是简单的布局错误。我更愿意把显示方式定义为业务选择,而不是用设备类型作强判断。NoticeLane 只有两个维度:compactPreview 表示当前演示处于窄栏观察状态,readable 表示用户要求完整静态阅读。后者的优先级更高。即使视觉区域还很窄,也不能因为宽度不够就自动把文字重新推回滚动状态。需要特别说明,本文的“阅读优先”是应用内显式开关,不是系统读屏状态识别接口。真实产品若要根据系统辅助功能设置自动调整,还应接入对应能力并验证可用范围;这里不借用未经确认的接口名称来拼装判断逻辑。当 readable=true,正文应在可用宽度内自然折行,容器高度随文本增长;当 readable=false 且 compactPreview=true,才把展示切换到 Marquee。在展开预览或普通详情里,静态文本仍是默认选项。这个简单优先级使调试时可以明确区分“布局压缩”和“用户主动选择的运动展示”。## 二、状态先行,组件只是两种表现为了让代码讨论聚焦,Demo 不读取真实工单接口,也不绑定设备折叠传感器。NoticePage.ets 持有提示原文、通知编号与演示布尔值;页面初始值与配图统一为 NL-083、NT-204、READABLE、320vp。这组数据是设计样本,不是某款设备的窗口测量值。要解决的问题,是把同一条字符串交给两种组件,而不是维持两套不同文案。下面这段展示核心声明和构建区。放到项目时可在 entry/src/main/ets/pages/NoticePage.ets 中作为页面骨架,并按实际路由结构补齐周边控件。arkts@Entry@Componentstruct NoticePage { @State private readable: boolean = true @State private compactPreview: boolean = true @State private active: boolean = true @State private noticeText: string = '资料等待业务复核,请确认附件完整性' private readonly taskId: string = 'NL-083' private readonly noticeId: string = 'NT-204' build() { Column({ space: 14 }) { Text('复核提醒').fontSize(22).fontWeight(FontWeight.Bold) if (this.readable || !this.compactPreview) { Text(this.noticeText).width('100%').fontSize('20fp') } else { Marquee({ start: this.active, step: 6, loop: -1, fromStart: true, src: this.noticeText }).width('100%').height(42).fontSize('20fp').allowScale(true) } Button(this.readable ? '阅读优先:已开启' : '阅读优先:已关闭') .onClick(() => this.setReadingPreference(!this.readable)) }.width('100%').padding(16) }}````Marquee` 是 ArkUI 的单行滚动组件,`start` 控制播放,`src` 是文本;`step` 与 `loop` 分别影响滚动步长和轮次。本文保留常见参数,不使用特定新版本才有的属性。`Text` 不设单行裁切,也不把字符串截断成短标题,它承担真正的阅读兜底。注意 `20fp` 与 `allowScale(true)` 只是让示例表达字体缩放意图;实际系统文字放大后的高度和断行还需设备验证。这里还有一个容易遗漏的差异:`Marquee` 在停止播放时仍是单行内容,不等于能完整阅读;所以“停止动画”不能取代切换到 `Text`。对可访问性更稳妥的基线,是把动态视图从组件树里切走,让完整文案持续存在,而不是让用户在一条静止但被截断的字符串上寻找答案。![](https://i-blog.csdnimg.cn/direct/d881eda1ecd543c09b3185ebff5ea7b2.png)## 三、窄栏预览不是折叠态检测为什么不直接写 `isFolded`?因为任务是保证通知可读,而不是演示一个折叠状态判断接口。折叠屏打开后可能是分栏、悬浮任务窗或外接大屏;直板手机开启大字号以后也可能比所谓折叠态更窄。把“设备形态”直接映射成“滚动开关”,会留下大量产品边界。演示中给出 `320vp` 的预览宽度只是观察输入。真正接入容器时应跟踪可用内容宽度,扣除左右留白,再决定卡片尺寸;这里不擅自把视口像素、物理像素和 vp 混为一谈。即使同一个页面的 `Text` 与 `Marquee` 占位相同,换行后的卡片高度也必然不同。底部操作区应由滚动容器容纳,不能靠固定页面高度硬压。接下来需要处理开关与页面切换。下面的方法应加入同一个 `NoticePage` 结构中,承担用户选择和日志记录,而不是让点击事件到处重复赋值。arktsprivate setReadingPreference(value: boolean): void { this.readable = value const mode: string = value ? ‘READABLE’ : ‘MARQUEE’ console.info([NoticeLane] task=${this.taskId} mode=${mode} width=320) console.info([NoticeLane] marqueeStart=${!value && this.active && this.compactPreview} notice=${this.noticeId})}private setCompactPreview(value: boolean): void { this.compactPreview = value if (!value) { this.setReadingPreference(true) }}````setReadingPreference 让同一次切换只有一个入口。marqueeStart 是由三个布尔状态推算出的演示值,不是调用底层服务成功的回执。setCompactPreview(false)主动回到READABLE,目的不是替用户永久修改偏好,而是防止详情进入宽布局后还被上一次演示的单行滚动牵制。如果产品希望“记住偏好”,应拆分会话临时态与可持久化偏好,不能把两者写在同一字段。当工单内容发生变化,还要考虑先前通知是否仍在播放。既然 src变动会改变滚动内容,不能让列表编号已更新而内部仍显示旧字符串;在这里最直接的处理是每次切换工单时先回到静态阅读,再设置新的通知 ID 与正文。这样不会虚构一个系统“强制重启动画”的 API。## 四、页面离开以后,停止条件也必须落地如果界面被隐藏,默认还让滚动组件一直有资格播放,至少增加了不必要的行为复杂度。对于这个没有实际音视频资源的 Demo,不需要发明一个destroyMarquee()方法;组件根据条件被移出树即可。生命周期上仍应明确暂停播放入口,避免未来扩展定时器或轮询时忘记收口。要解决的是页面可见性与播放开关的对应关系。下面两个页面生命周期方法仍放在NoticePage内,逻辑保持尽可能短。```arktsonPageHide(): void { this.active = false console.info('[NoticeLane] page=HIDDEN marqueeStart=false')}onPageShow(): void { this.active = true console.info('[NoticeLane] page=VISIBLE')}```页面隐藏后active=false,使 Marquee的start条件失效;恢复页面时重新评估readable与compactPreview,而非无条件开启动画。当前示例没有网络请求、播放句柄或定时任务,因此也没有需要释放的外部资源。若未来引入定时采集布局指标,监听注册与注销就必须成对出现。生命周期方法适用范围要以页面是否真正进入路由栈的行为核验,组件嵌套复用场景不能直接假设这些回调都会触发。## 五、验收时关注“能看见什么”,别用滚动帧率代替信息完整本轮演示图展示的是 READABLE模式:卡片标题是“复核提醒”,通知NT-204 的全文自然折行,阅读优先:已开启按钮可见;顶部时间设为10:48,电量 92%。DevEco 风格配图里的 HiLog 也是预设文本。它们说明**应该观察哪些字段**,不代表运行设备已产生这些记录。![](https://i-blog.csdnimg.cn/direct/662a786bf4064ae6acaa22fe14e9e6eb.png)验证顺序建议先开窄栏预览,再连续切换两次阅读开关,随后把文字换成长短不同的样本,最后在页面隐藏与恢复之间再切一次。需要观察的是正文是否完整、操作按钮是否仍可操作,以及工单 ID 与文案是否属于同一条记录。对真实折叠设备,还要补上大字模式、横竖屏、外接显示和辅助功能开启下的回归,而不是拿一张静态效果图替代测试矩阵。若回归发现静态文案突然只剩一行,先查 readable、compactPreview与当前组件分支,不要把问题直接归到Marquee的速度参数。若文字被裁在卡片底部,查容器是否固定高度、是否设置了上限、外层是否允许滚动。若动画异常地继续活动,查active的生命周期变化,以及页面是否仍作为可见子组件存在。## 六、这里真正要固定的是用户的阅读控制权该方案不提供自动识别屏幕阅读器的功能,不承诺所有字体倍率下的布局结果,也没有在系统闪控窗里直接调用特殊窗口 API。它处理的是业务 UI 层面“动态文本展示应有静态兜底”这一小而明确的合同。设备形态如何变化,可以影响默认排版建议,却不应该夺走用户已经明确选择的阅读方式。如果以后把 NoticeLane 放进真正的双栏业务工作台,左栏可以继续展示简短摘要,右栏坚持完整Text;若必须保留滚动版,应增加可见的停止入口和完整内容的可达路径。至于性能,不应把减少一个 Marquee` 当作已量化收益,仍要在真实设备上观察刷新、功耗和交互反馈。本文的完成条件是状态和边界清晰,真实验证仍是下一步。官方参考:华为开发者文档《Marquee》、ArkUI《Text》及文本展示说明:- https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-marquee- https://developer.huawei.com/consumer/en/doc/harmonyos-guides-V13/arkts-common-components-text-display-V13配图均为原创界面演示,不是 IDE 实拍或真机测量记录。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐