HarmonyOS 折叠屏悬停态适配详解:三种实现方式完整指南
一、悬停态是啥玩意儿
折叠屏有个独特的手持操作体验叫"悬停态"。啥意思?用户可以把设备半折后立在桌面上,实现免手持体验。
悬停态适用于不需要频繁交互的任务:
- 视频通话
- 视频播放
- 拍照
- 听歌
进入悬停态时,有个问题:中间弯折区域难以操作,且显示内容会变形。所以页面内容要进行折痕区避让适配。

上图展示了折叠屏悬停态的使用场景,设备半折立在桌面上,上半屏显示视频,下半屏显示控制按钮。
二、三种实现方式对比
实现悬停态有三种方式:
| 对比项 | FolderStack | FoldSplitContainer | 自定义实现 |
|---|---|---|---|
| 展开态/折叠态是否支持自定义布局 | 支持 | 不支持,固定二分栏/三分栏 | 支持 |
| 是否支持由其他页面进入悬停态页面 | 支持 | 支持 | 支持 |
| 是否支持自定义设备状态进入悬停态页面 | 不支持 | 不支持 | 支持 |
| 是否支持自定义悬停态窗口旋转策略 | 不支持 | 不支持 | 支持 |
| 开发难度 | 简单 | 简单 | 困难 |
三种方式各有特点:
- FolderStack:使用简单,无需关注设备状态,支持自定义页面布局
- FoldSplitContainer:使用简单,但固定的二分栏和三分栏布局限制了使用场景
- 自定义实现:需要自行监听设备状态并调整组件布局,支持自定义布局,且由于自实现悬停态监听,可以限制设备进入悬停态的场景(例如仅允许在横屏下半折叠时进入悬停态)以及自定义窗口旋转策略,使用更加灵活

三、FolderStack 实现:最简单的方式
实现原理
FolderStack 是系统提供的 ArkTS 组件,继承自层叠布局 Stack。在 Stack 组件的基础上,FolderStack 提供监控设备是否进入悬停态并进行重新布局的能力。
FolderStack 通过 upperItems 字段来实现悬停态布局:
- 被 upperItems 字段修饰的组件会堆叠在上半屏
- 其他未被修饰的组件会堆叠在下半屏,并且自动避让折叠屏折痕区
注意:FolderStack 需要撑满页面全屏,如果不撑满页面全屏,则只作为普通 Stack 使用。
适用场景
适用于视频全屏播放等交互少的场景。
开发步骤
以视频播放类应用的全屏播放页面为例,将页面的父容器设置为 FolderStack,并将视频播放组件的 ID 注册到 upperItems 数组中。
悬停态时:
- 视频播放组件会自动调整到上半屏显示
- 视频控制组件和顶部返回组件则显示在下半屏

代码实现:
FolderStack({ upperItems: ['upper'] }) {
// 视频播放组件,显示在上半屏
VideoPlayView({ avPlayerUtil: this.avPlayerUtil })
.id('upper')
// 视频控制组件,显示在下半屏
VideoControlView({ avPlayerUtil: this.avPlayerUtil })
// 顶部返回组件,显示在下半屏
BackTitleView({
title: Const.PAGE_TITLES[0]
})
}
关键点:
- upperItems 数组中注册组件 ID(这里是 ‘upper’)
- 视频播放组件的 id 设为 ‘upper’,就会被移到上半屏
- 其他组件没有被 upperItems 修饰,就会显示在下半屏并自动避让折痕区
四、FoldSplitContainer 实现:固定分栏布局
实现原理
FoldSplitContainer 是系统提供的分栏类型的 ArkTS 组件,可以实现折叠屏二分栏、三分栏在展开态、悬停态以及折叠态的区域控制。
- 二分栏:上下分栏
- 三分栏:在二分栏基础上加上侧边栏
FoldSplitContainer 的参数:
- primary:设置二分栏的上区域布局
- secondary:设置二分栏的下区域布局
- extra:设置三分栏中侧栏区域的布局
- LayoutOptions:设置各区域分栏的比例
当设备进入悬停态时,FoldSplitContainer 会自动避让折叠屏折痕区。

适用场景
适用于分栏显示内容的场景,例如游戏画面和操作区域。
开发步骤
以游戏界面为例,将上下屏的组件分别注册到 primary 和 secondary 参数的回调中。
代码实现:
FoldSplitContainer({
primary: () => {
this.primaryArea(); // 上半屏内容
},
secondary: () => {
this.secondaryArea(); // 下半屏内容
}
})
这里只实现了二分栏结构,未实现 extra 参数对应的侧栏。

五、自定义实现:最灵活的方式
实现原理
自定义悬停态布局需要在折叠屏进入半折叠态时:
- 设置窗口横向显示
- 规避折痕避让区
- 调整页面内组件的尺寸和位置
分为两部分:
- 监听悬停态:通过 display.on(‘foldStatusChange’) 接口监听设备是否进入半折叠态,同时通过 display 的 orientation 属性判断设备是否横屏,当两种状态都满足时即判断设备进入悬停态
- 调整布局:当设备进入悬停态后,通过 display.getCurrentFoldCreaseRegion() 接口获取折叠屏折痕区域的位置和大小,计算并设置上下半屏组件的尺寸和位置完成悬停态布局
注意:在退出应用或者退出需要监听折叠态变化的页面时,需要调用 display.off(‘foldStatusChange’) 接口取消监听,避免出现意想不到的问题。
适用场景
适用于页面布局复杂和悬停态触发动作自定义的场景。
开发步骤
1. 监听悬停态
悬停态通过状态变量 isHover 进行监听。当折叠屏的折叠状态变化时,判断当前是否为悬停态并更新 isHover 的值。
定义监听折叠状态变化回调方法:
private onFoldStatusChange: Callback<display.FoldStatus> = (data: display.FoldStatus) => {
try {
let orientation: display.Orientation = display.getDefaultDisplaySync().orientation;
if (this.pageID === 0 || this.pageID === 3) {
if (data === display.FoldStatus.FOLD_STATUS_HALF_FOLDED &&
this.currentWidthBreakpoint === Const.BREAKPOINT_MD &&
(orientation === display.Orientation.LANDSCAPE ||
orientation === display.Orientation.LANDSCAPE_INVERTED)) {
this.isHover = true;
// ...
} else {
this.isHover = false;
}
}
} catch (error) {
hilog.error(0x0000, TAG, `onFoldStatusChange catch error, code: ${error.code}, message: ${error.message}`);
}
};
判断逻辑:
- FoldStatus 是 FOLD_STATUS_HALF_FOLDED(半折叠态)
- 横向断点是 BREAKPOINT_MD
- orientation 是 LANDSCAPE 或 LANDSCAPE_INVERTED(横屏)
三个条件都满足,才判定为悬停态。
在 display 中注册方法,监听设备折叠状态变化:
try {
display.on('foldStatusChange', this.onFoldStatusChange);
} catch (exception) {
hilog.error(0x0000, TAG, 'Failed to register onFoldStatusChange callback. Code: ' + JSON.stringify(exception));
}
2. 获取折痕区信息
当设备处于悬停状态(isHover 为 true)时,页面内组件需要获取折痕区的大小和位置:
static getFoldCreaseRegion(): void {
try {
if (display.isFoldable()) {
let foldRegion: display.FoldCreaseRegion = display.getCurrentFoldCreaseRegion();
let rect: display.Rect = foldRegion.creaseRects[0];
// 折痕区上边界位置和折痕区高度
let creaseRegion: number[] = [uiContext!.px2vp(rect.top), uiContext!.px2vp(rect.height)];
AppStorage.setOrCreate('creaseRegion', creaseRegion);
}
} catch (error) {
hilog.error(0x0000, TAG, `getFoldCreaseRegion catch error, code: ${error.code}, message: ${error.message}`);
}
}
返回的 creaseRegion 数组:
- creaseRegion[0]:折痕区上边界位置(上半屏高度)
- creaseRegion[1]:折痕区高度(需要避让的区域高度)
3. 调整组件布局
根据折痕区的大小和位置调整布局:
视频播放组件:将上移至屏幕上方
Column() {
XComponent({
id: Const.X_COMPONENT_ID,
type: XComponentType.SURFACE,
controller: this.xComponentController
})
// ...
}
.height(this.isHover ? this.creaseRegion[0] : '100%')
悬停态时,视频播放组件高度设为折痕区上边界位置,刚好填满上半屏。
视频控制组件:位于下半屏,无需调整
顶部返回组件:移动到屏幕下半部分的顶部
Row() {
// ...
}
.width('80%')
.height('24vp')
.justifyContent(FlexAlign.Start)
.position({
x: '24vp',
y: this.isHover ? this.creaseRegion[0] + this.creaseRegion[1] + 36 : '36vp'
})
悬停态时,返回组件的 y 坐标设为折痕区上边界 + 折痕区高度 + 36vp,刚好显示在下半屏顶部,避开了折痕区。

六、三种方式选用建议
| 场景 | 推荐方式 |
|---|---|
| 视频全屏播放,交互少 | FolderStack |
| 游戏界面,画面和操作分栏显示 | FoldSplitContainer |
| 页面布局复杂,需要自定义悬停触发条件 | 自定义实现 |
| 需要限制悬停态场景(如仅横屏半折叠) | 自定义实现 |
| 需要自定义窗口旋转策略 | 自定义实现 |
简单场景用系统组件:
- FolderStack:最简单,只需设置 upperItems
- FoldSplitContainer:固定分栏布局,适合游戏
复杂场景自定义实现:
- 可以限制悬停触发条件
- 可以自定义窗口旋转策略
- 但需要监听设备状态,开发难度大
七、踩坑记录
坑 1:FolderStack 必须撑满全屏
FolderStack 需要撑满页面全屏,如果不撑满页面全屏,则只作为普通 Stack 使用,悬停态不生效。
设置 FolderStack 的宽高:
FolderStack({ upperItems: ['upper'] }) {
// ...
}
.width('100%')
.height('100%')
坑 2:FoldSplitContainer 的布局限制
FoldSplitContainer 只支持固定的二分栏和三分栏布局:
- 不能自定义布局比例(只能通过 LayoutOptions 设置预设比例)
- 不能自定义区域数量
如果需要自定义布局,就得用自定义实现。
坑 3:自定义实现要取消监听
在退出应用或者退出需要监听折叠态变化的页面时,必须调用 display.off(‘foldStatusChange’) 取消监听。
不取消的话,会继续监听设备状态变化,可能导致意外的问题。
取消监听:
display.off('foldStatusChange', this.onFoldStatusChange);
坑 4:判断悬停态要同时满足多个条件
悬停态判断要同时满足:
- FoldStatus 是 FOLD_STATUS_HALF_FOLDED
- 横向断点是 BREAKPOINT_MD
- orientation 是 LANDSCAPE 或 LANDSCAPE_INVERTED
只判断 FoldStatus 不够,因为设备可能在竖屏下半折叠,此时不是悬停态。
坑 5:折痕区高度要用 px2vp 转换
display.getCurrentFoldCreaseRegion() 返回的尺寸单位是 px,需要用 px2vp 转换为 vp。
直接用 px 的话,布局尺寸会不对。
转换:
let creaseRegion: number[] = [uiContext!.px2vp(rect.top), uiContext!.px2vp(rect.height)];
八、总结
折叠屏悬停态适配有三种方式:
- FolderStack:最简单,通过 upperItems 指定上半屏组件,其他组件自动显示在下半屏并避让折痕区
- FoldSplitContainer:固定分栏布局,适合游戏画面和操作区域分栏
- 自定义实现:最灵活,可以自定义悬停触发条件和窗口旋转策略,但开发难度大
简单场景用系统组件,复杂场景自定义实现。
关键是理解悬停态的特点:上半屏显示内容,下半屏显示控制,中间折痕区要避让。
掌握了这三种方式,折叠屏悬停态适配就不是啥难事了。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)