关怀模式初值查询与实时监听封面

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)
  }
}

这里有三个不能省的点:

  1. 订阅和取消订阅传入同一个回调实例,不要在 offSeniorModeStateChange 里临时再写一个箭头函数。
  2. 查询完成时比较 eventVersion。若中途已经收到系统事件,页面上的是较新状态,查询结果不再覆盖它。
  3. 页面消失后把 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):

Logo

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

更多推荐