HarmonyOS 7 关怀模式进页后才变大字?初值查询与状态监听要一起做

HarmonyOS 7 关怀模式进页后才变大字?初值查询与状态监听要一起做
做关怀模式适配时,一个容易漏掉的细节是:监听器只告诉你后面发生了什么,不会替你补发页面出现前的当前状态。如果页面默认普通字号,用户早已在系统设置中打开关怀模式,页面可能直到下一次切换才更新。反过来,只在进页时查询一次,用户在应用运行期间切换系统开关,页面又不会跟着变。
HarmonyOS 7 对应的 API 26.0.0 提供了 isSeniorModeEnabled()、onSeniorModeStateChange() 和 offSeniorModeStateChange()。这篇只解决一个问题:页面怎样拿到正确初值,并持续跟随系统开关,同时避免异步查询覆盖较新的回调结果。这里的“大字”是演示效果,不等于完整的关怀模式设计。
版本边界:下述接口从 API 26.0.0 开始提供。示例使用 Stage 模型页面;若项目仍需在较低 API 版本运行,不能直接把这些调用当成所有设备都支持的能力。本文的状态合并测试在本机运行,API 26 工程编译及真机开关测试尚未完成,不把它们写成已验证结果。
两个能复现的问题
案例一:只订阅,启动时还是小字。 先在系统设置中打开关怀模式,再启动应用。页面初始值仍是 false;如果系统没有再发生开关变化,订阅回调就没有机会修正这次渲染。解决办法是进页订阅后再读一次当前值。
案例二:只查询,运行中不更新。 页面打开时读到了 false,随后切到设置打开关怀模式,再回到应用。没有订阅回调,页面仍显示旧状态。解决办法是保留一份与页面生命周期绑定的监听,并在退出页面时取消。
还有一个更隐蔽的时序:进页发起异步查询后,用户立刻切换了系统开关。如果旧查询晚于新事件返回,不能让旧值盖掉新值。 下面用递增序号判断“查询开始之后是否收到过事件”。
页面代码:先订阅,再读初值
import { accessibility } from '@kit.AccessibilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
@Entry
@Component
struct SeniorModePage {
@State private seniorEnabled: boolean = false;
@State private status: string = '读取系统状态中';
private attached: boolean = false;
private eventVersion: number = 0;
private onSeniorChanged = (enabled: boolean): void => {
if (!this.attached) {
return;
}
this.eventVersion += 1;
this.seniorEnabled = enabled;
this.status = '跟随系统开关';
};
aboutToAppear(): void {
this.attached = true;
const versionAtQuery = this.eventVersion;
accessibility.onSeniorModeStateChange(this.onSeniorChanged);
accessibility.isSeniorModeEnabled()
.then((enabled: boolean) => {
if (!this.attached || this.eventVersion !== versionAtQuery) {
return;
}
this.seniorEnabled = enabled;
this.status = '已读取系统初值';
})
.catch((error: BusinessError) => {
if (this.attached && this.eventVersion === versionAtQuery) {
this.status = `读取失败:${error.code}`;
}
});
}
aboutToDisappear(): void {
this.attached = false;
accessibility.offSeniorModeStateChange(this.onSeniorChanged);
}
build() {
Column({ space: 16 }) {
Text(this.seniorEnabled ? '关怀模式已开启' : '普通模式')
.fontSize(this.seniorEnabled ? 28 : 18)
Text(this.status).fontSize(14)
}
.width('100%')
.padding(24)
}
}
这里有三个不能省的点:
- 订阅和取消订阅传入同一个回调实例,不要在
offSeniorModeStateChange里临时再写一个箭头函数。 - 查询完成时比较
eventVersion。若中途已经收到系统事件,页面上的是较新状态,查询结果不再覆盖它。 - 页面消失后把
attached设为false。即使异步查询在取消监听后才完成,也不再改写已离开的页面。

本机怎样验证状态合并逻辑
系统接口需要 API 26 SDK 与实际设备或模拟器。没有这套环境时,至少可以先把最容易写错的查询与事件顺序做成本机测试。下面的测试只验证合并策略,不能替代 ArkTS 编译或系统开关实测。
import assert from 'node:assert/strict';
class SeniorModeState {
constructor() {
this.active = false;
this.version = 0;
this.enabled = false;
}
appear() {
this.active = true;
return this.version;
}
event(enabled) {
if (!this.active) return;
this.version += 1;
this.enabled = enabled;
}
queryResult(enabled, startedAt) {
if (!this.active || this.version !== startedAt) return;
this.enabled = enabled;
}
disappear() {
this.active = false;
}
}
const initial = new SeniorModeState();
const a = initial.appear();
initial.queryResult(true, a);
assert.equal(initial.enabled, true); // 系统已开:进页读取初值
const live = new SeniorModeState();
const b = live.appear();
live.queryResult(false, b);
live.event(true);
assert.equal(live.enabled, true); // 页面仍在:开关实时变化
const race = new SeniorModeState();
const c = race.appear();
race.event(true);
race.queryResult(false, c);
assert.equal(race.enabled, true); // 晚到的旧查询不覆盖事件
const gone = new SeniorModeState();
const d = gone.appear();
gone.disappear();
gone.queryResult(true, d);
assert.equal(gone.enabled, false); // 离页后不写回
console.log('4 checks passed');
把这段代码保存为 senior-mode-state.mjs,运行 node senior-mode-state.mjs,应看到 4 checks passed。真正上设备时还应依次测试:系统开关先开再启动、页面运行中开关各一次、反复进出页面、查询失败、以及应用后台返回。记录屏幕字号与回调日志,不能只看页面上的一句“已开启”。
要不要声明 senior_mode metadata?
不要把“跟随系统开关”和“应用在系统关怀模式管理页里提供独立开关”混为一谈。官方的接入说明中,module.json5 的 senior_mode=independent_control 是给已经实现独立关怀模式功能的应用声明使用的。本文的最小例子只是读取和监听系统状态,不应为了让示例看起来完整就乱加这段声明。真要做独立开关,需要再核对系统设置同步行为和完整的应用内适老方案。
最后检查
如果只在启动时显示正确,优先查有没有漏掉订阅;如果运行中能变、首次进页却不对,优先查有没有读取初值;如果偶尔从大字跳回小字,记录“事件发生时间”和“查询完成时间”,检查旧查询覆盖。只有这三种路径都稳住,才值得继续做字号、触控区、对比度与焦点顺序的完整适配。
官方资料(核对日期:2026-09-18):
更多推荐



所有评论(0)