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() → 框架用,跨进程恢复滚动

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

相关推荐
全栈若城6 天前
HarmonyOS6 半年磨一剑 - RcInput 组件核心架构与类型系统设计
架构·harmonyos6·三方库开发实战·rchoui·三方库开发
全栈若城1 个月前
HarmonyOS6 半年磨一剑 - RcInput 组件清空、密码切换与图标交互机制
架构·交互·harmonyos6·三方库开发实战·rchoui·三方库开发
全栈若城1 个月前
HarmonyOS 6 实战:Component3D 与 SURFACE 渲染模式深度解析
3d·架构·harmonyos6
全栈若城1 个月前
HarmonyOS 6 实战:使用 ArkGraphics3D 加载 GLB 模型与 Scene 初始化全流程
3d·华为·架构·harmonyos·harmonyos6
是稻香啊1 个月前
HarmonyOS6 ArkTS Popup 气泡组件指南
harmonyos6
是稻香啊1 个月前
HarmonyOS6 触摸目标 touch-target 属性使用指南
harmonyos6
是稻香啊1 个月前
HarmonyOS6 foregroundBlurStyle 通用属性使用指南
harmonyos6
是稻香啊1 个月前
HarmonyOS6 clickEffect 通用属性使用指南
harmonyos6
是稻香啊1 个月前
HarmonyOS6 filter 通用属性使用指南
harmonyos6
是稻香啊2 个月前
HarmonyOS6 ArkUI 无障碍悬停事件(onAccessibilityHover)全面解析与实战演示
华为·harmonyos·harmonyos6