HarmonyOS6 ArkUI .restoreId() 滚动位置恢复全解析

文章目录

    • 一、三个组件标识属性的定位
    • 二、属性签名与参数说明
    • [三、为什么需要 restoreId?](#三、为什么需要 restoreId?)
    • [四、前置配置:module.json5 开启状态恢复](#四、前置配置:module.json5 开启状态恢复)
    • [五、示例代码解析(对应 index.ets RestoreIdExample)](#五、示例代码解析(对应 index.ets RestoreIdExample))
      • [5.1 完整源码](#5.1 完整源码)
      • [5.2 标注解析](#5.2 标注解析)
    • 六、唯一性规则与常见错误
      • [6.1 同页面内必须唯一](#6.1 同页面内必须唯一)
      • [6.2 ForEach 动态渲染多个可滚动组件](#6.2 ForEach 动态渲染多个可滚动组件)
      • [6.3 对非滚动组件设置(无效但不报错)](#6.3 对非滚动组件设置(无效但不报错))
    • [七、与 .id() 联合使用](#七、与 .id() 联合使用)
    • [八、与 .id() / .key() 的完整三属性对比](#八、与 .id() / .key() 的完整三属性对比)
    • 十、总结

一、三个组件标识属性的定位

ArkUI 提供了三个组件标识通用属性,各自面向不同场景。理解它们的边界,是写出规范业务代码的前提:

属性 签名 参数类型 API 版本 核心用途
.id() id(value: string): T string API 10+ 业务代码唯一标识,供 getFrameNodeById 查询布局
.key() key(value: string): T string API 10+ ForEach diff key / 测试标识(getInspectorByKey 仅限测试文件)
.restoreId() restoreId(value: number): T number API 8+ 应用被系统回收后重建时,自动恢复可滚动组件的滚动位置

index.ets 顶部注释对此做了完整说明:

typescript 复制代码
// ① .id(string)         ------ 唯一字符串 ID,供 FrameNode / componentUtils 查询布局
// ② .key(string)        ------ ForEach diff key,帮助框架复用组件节点
// ③ .restoreId(number)  ------ 整数恢复标识,应用被系统回收后重建时自动恢复
//                           可滚动组件(List / Scroll / Grid / WaterFlow)的滚动位置

重点讲解 .restoreId(),上图中的第三个属性。


二、属性签名与参数说明

typescript 复制代码
restoreId(value: number): T
参数 类型 必填 说明
value number 非负整数,同一页面内必须唯一;框架以此整数关联快照数据与组件实例

适用组件范围(仅以下可滚动组件支持状态恢复):

组件 恢复的状态内容
List 滚动偏移量(上次停留的位置)
Scroll 滚动偏移量
Grid 滚动偏移量
WaterFlow 滚动偏移量

ColumnRowTextButton 等非滚动组件设置 .restoreId() 不会报错,但也不会有任何效果


三、为什么需要 restoreId?

HarmonyOS 会在低内存时主动回收后台应用进程。进程销毁后,用户再次打开应用时,Ability 从零重建,页面恢复到初始状态,用户上次的滚动位置随之丢失

.restoreId() 通过框架内置的快照机制解决这个问题,整个流程如下:

复制代码
用户正常使用
  → 将列表滚动到第 80 条
  → 接到来电 / 切到其他应用 → 系统内存不足 → 进程被回收
  → 框架在回收前已将 restoreId=1 对应的滚动位置写入快照

用户重新打开应用
  → Ability 重建,页面重建
  → 框架检测到 restoreId=1 有快照数据
  → List 滚动自动恢复到第 80 条位置
  → 用户无感知,体验连续

与手动保存方案对比:

方案 监听滚动事件 持久化存储 重建时读取恢复 代码量
手动(AppStorage / Preferences) ✅ 需要 ✅ 需要 ✅ 需要 较多
.restoreId() ❌ 不需要 ❌ 不需要 ❌ 不需要 一行

四、前置配置:module.json5 开启状态恢复

.restoreId() 依赖 Ability 级别的状态恢复开关。未开启时,框架不会记录快照,属性声明了也不会生效:

json5 复制代码
// entry/src/main/module.json5
{
  "module": {
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntry": "./ets/entryability/EntryAbility.ets",
        "restoreEnabled": true   // ← 必须添加此字段,restoreId 才会生效
      }
    ]
  }
}

五、示例代码解析(对应 index.ets RestoreIdExample)

5.1 完整源码

typescript 复制代码
@Entry
@Component
struct RestoreIdExample {
  // 模拟列表数据:30 条
  private listData: number[] = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29];

  build() {
    Column({ space: 0 }) {
      Text('restoreId 示例:滚动位置恢复')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .padding({ top: 16, bottom: 8 })

      List({ space: 12 }) {
        ForEach(
          this.listData,
          (item: number) => {
            ListItem() {
              Text(`列表项 ${item}`)
                .width('100%')
                .height(60)
                .fontSize(16)
                .textAlign(TextAlign.Center)
                .borderRadius(8)
                .backgroundColor(Color.Pink)
            }
          },
          // ForEach 第三个参数:keyGenerator
          // 为每个 ListItem 提供唯一 key,对应 .key() 概念
          (item: number) => item.toString()  // ← ②
        )
      }
      .width('100%')
      .layoutWeight(1)
      .restoreId(1)   // ← ① 状态恢复标识,同页面内唯一整数
    }
    .width('100%')
    .height('100%')
    .padding({ left: 16, right: 16 })
  }

}

运行结果如图:

5.2 标注解析

标注 ①:.restoreId(1)

链式挂载到 List 组件。参数 1 是页面内的唯一整数标识,框架通过这个整数将滚动快照与具体 List 实例绑定。写法与 .width().layoutWeight() 完全一致,无需其他代码。

标注 ②:ForEach keyGenerator(第三个参数)

typescript 复制代码
(item: number) => item.toString()

这是 .key() 属性的 ForEach 等价形式。框架在 diff 时用此返回值判断哪些 ListItem 是新增/删除/移动的,从而精确复用节点,避免整列表重建。

.restoreId() 与 keyGenerator 的协作关系:

复制代码
.restoreId(1)      → 框架记住"这个 List 滚动到了哪里"
keyGenerator       → 框架知道"恢复滚动后,每一项对应哪个数据"
两者配合,列表恢复后内容与位置均与退出前一致

六、唯一性规则与常见错误

6.1 同页面内必须唯一

typescript 复制代码
// ❌ 错误:两个可滚动组件使用相同的 restoreId,框架行为不可预期
List(...).restoreId(1)
Grid(...).restoreId(1)   // 重复!

// ✅ 正确:各自分配不同整数
List(...).restoreId(1)
Grid(...).restoreId(2)

6.2 ForEach 动态渲染多个可滚动组件

typescript 复制代码
// ❌ 错误:动态生成的多个 List 全部用 restoreId(1)
ForEach(tabs, (tab: Tab) => {
  List(...).restoreId(1)   // 每个 List 的 restoreId 相同,互相覆盖快照
})

// ✅ 正确:用 index 加偏移量确保唯一(偏移避免与页面其他组件冲突)
ForEach(tabs, (tab: Tab, index: number) => {
  List(...).restoreId(100 + index)   // 100、101、102 ...
})

6.3 对非滚动组件设置(无效但不报错)

typescript 复制代码
// ⚠ 不生效:Text、Button 等非滚动组件设置 restoreId 没有任何效果
Text('标题').restoreId(5)   // 框架忽略,不报错,也不恢复任何状态

七、与 .id() 联合使用

.restoreId().id() 面向不同场景,可同时设置,互不干扰:

typescript 复制代码
List({ space: 12 }) {
  // ...
}
.id('mainList')    // ← 供业务代码通过 getFrameNodeById 查询布局位置
.restoreId(1)      // ← 供框架在 Ability 重建时恢复滚动位置

两个属性的生命周期完全不同:

属性 谁来使用 使用时机
.id('mainList') 开发者主动调用 布局完成后,随时通过 getFrameNodeById('mainList') 查询
.restoreId(1) 框架自动处理 Ability 被回收前(写快照)、重建后(读快照恢复),开发者无需介入

八、与 .id() / .key() 的完整三属性对比

对比项 .id(string) .key(string) .restoreId(number)
参数类型 string string number
API 版本 API 10+ API 10+ API 8+
适用组件 所有组件 所有组件 仅可滚动组件
主要用途 业务查询布局 ForEach diff / 测试标识 跨进程状态恢复
生效时机 布局完成后,按需查询 渲染 diff 时 / 测试触发时 Ability 被回收并重建时
ArkTSCheck ✅ 无警告 ⚠ 配合测试 API 在业务代码中报警告 ✅ 无警告
需要配置文件 ✅ module.json5 restoreEnabled: true

十、总结

.restoreId() 是 ArkUI 三个标识属性中使用成本最低 的一个:只需一行链式调用,配合 module.json5 中一个配置字段,就能让可滚动组件在应用进程被系统回收后无缝恢复滚动位置,全程不需要监听、存储、读取。

结合 index.etsRestoreIdExample 的示例,三个标识属性的协作关系可以总结为下面这段代码:

typescript 复制代码
List({ space: 12 }) {
  ForEach(
    this.listData,
    (item: number) => { ListItem() { ... } },
    (item: number) => item.toString()  // ← .key() 等价:ForEach diff key
  )
}
.id('mainList')   // ← .id():供 getFrameNodeById 查询布局
.restoreId(1)     // ← .restoreId():供框架恢复滚动位置

三行代码,三个维度,各司其职:

  • .id() → 开发者用,随时查位置
  • ForEach keyGenerator(.key() 等价)→ 框架用,精准 diff 节点
  • .restoreId() → 框架用,跨进程恢复滚动

如果这篇文章对你有帮助,欢迎点赞、收藏、关注,你的支持是持续创作的动力!

相关推荐
是稻香啊5 小时前
HarmonyOS6 ArkUI 子组件触摸测试控制(onChildTouchTest)全面解析与实战演示
华为·harmonyos·harmonyos6
是稻香啊11 小时前
HarmonyOS6 overlay 叠加层属性使用指南
harmonyos6
是稻香啊14 小时前
HarmonyOS6 ArkUI 无障碍事件(Accessibility Event)全面解析与实战演示
harmonyos6
是稻香啊14 小时前
HarmonyOS6 ArkUI 组件区域变化事件(onAreaChange)全面解析与实战演示
harmonyos6
是稻香啊20 小时前
HarmonyOS6 组件显隐事件(onAppear / onDisAppear / onAttach / onDetach)
harmonyos6
是稻香啊1 天前
HarmonyOS6 ArkUI 组件尺寸变化事件(onSizeChange)全面解析与实战演示
harmonyos6
ITUnicorn21 天前
【HarmonyOS 6】进度组件实战:打造精美的数据可视化
华为·harmonyos·arkts·鸿蒙·harmonyos6
ITUnicorn25 天前
【HarmonyOS 6】数据可视化:实现热力图时间块展示
华为·harmonyos·arkts·鸿蒙·harmonyos6
ITUnicorn1 个月前
【HarmonyOS 6】HarmonyOS 自定义时间选择器实现
华为·harmonyos·arkts·鸿蒙·harmonyos6