HarmonyOS 5 鸿蒙应用横竖屏切换完全指南
本文将深入讲解在 HarmonyOS 5 中如何控制应用的横竖屏切换,包括静态配置、动态控制、监听方向变化以及多设备适配的最佳实践。
一、概述
横竖屏切换是移动应用开发中的常见需求。在 HarmonyOS 中,屏幕方向管理涉及多个层面:
-
Ability 配置:通过
module.json5配置页面的默认方向 -
窗口管理:通过
windowAPI 动态控制窗口方向 -
布局适配:根据方向变化动态调整 UI 布局
-
事件监听:监听方向变化并做出响应
HarmonyOS 5 提供了灵活的屏幕方向管理能力,支持从简单的固定方向到复杂的多设备自适应策略。
二、静态配置屏幕方向
2.1 module.json5 配置
在 module.json5 文件的 abilities 标签中,通过 orientation 属性配置页面的默认方向:
json
{
"module": {
"abilities": [
{
"name": "EntryAbility",
"orientation": "landscape" // 强制横屏
}
]
}
}
2.2 orientation 可选值
| 值 | 说明 |
|---|---|
unspecified |
系统自动判断(默认) |
portrait |
固定竖屏 |
landscape |
固定横屏 |
auto_rotation |
自动旋转 |
auto_rotation_restricted |
受限自动旋转 |
landscape_inverted |
反向横屏 |
portrait_inverted |
反向竖屏 |
2.3 应用场景
-
视频播放器:全局设置为
landscape,强制横屏播放 -
阅读类应用:全局设置为
portrait,保持竖屏阅读 -
通用应用:设置为
unspecified,允许系统自动适配
三、动态控制横竖屏切换
3.1 核心 API
使用 window.setPreferredOrientation() 方法动态控制窗口方向:
typescript
import { window } from '@kit.ArkUI';
// 获取窗口对象
let windowStage = AppStorage.get('windowStage') as window.WindowStage;
let mainWindow = windowStage.getMainWindowSync();
// 切换为横屏
mainWindow.setPreferredOrientation(window.Orientation.LANDSCAPE);
// 切换为竖屏
mainWindow.setPreferredOrientation(window.Orientation.PORTRAIT);
// 开启自动旋转
mainWindow.setPreferredOrientation(window.Orientation.AUTO_ROTATION);
3.2 在页面中控制方向
在 ArkTS 页面中,通过 onPageShow 或 aboutToAppear 生命周期控制方向:
typescript
import { window } from '@kit.ArkUI';
@Entry
@Component
struct VideoPlayerPage {
private windowStage: window.WindowStage =
AppStorage.get('windowStage') as window.WindowStage;
private mainWindow: window.Window = this.windowStage.getMainWindowSync();
onPageShow() {
// 进入页面时强制横屏
this.mainWindow.setPreferredOrientation(window.Orientation.LANDSCAPE);
}
onPageHide() {
// 离开页面时恢复竖屏
this.mainWindow.setPreferredOrientation(window.Orientation.PORTRAIT);
}
build() {
// 页面内容
}
}
3.3 使用回调处理结果
typescript
try {
mainWindow.setPreferredOrientation(
window.Orientation.AUTO_ROTATION,
(err: BusinessError) => {
if (err.code) {
console.error('设置方向失败: ' + JSON.stringify(err));
return;
}
console.info('设置方向成功');
}
);
} catch (exception) {
console.error('设置方向异常: ' + JSON.stringify(exception));
}
四、监听横竖屏变化
4.1 方法一:使用媒体查询(推荐)
mediaquery API 是监听屏幕方向变化的标准方式:
typescript
import { mediaquery } from '@kit.ArkUI';
@Entry
@Component
struct ScreenRotationPage {
@State isLandscape: boolean = false;
private listener?: mediaquery.MediaQueryListener;
aboutToAppear() {
// 创建横屏监听器
this.listener = mediaquery.matchMediaSync('(orientation: landscape)');
// 注册回调
this.listener.on('change', (result: mediaquery.MediaQueryResult) => {
this.isLandscape = result.matches;
console.log(`屏幕方向: ${this.isLandscape ? '横屏' : '竖屏'}`);
});
}
aboutToDisappear() {
// 移除监听器
if (this.listener) {
this.listener.off('change');
}
}
build() {
Column() {
Text(this.isLandscape ? '当前为横屏' : '当前为竖屏')
.fontSize(20)
}
.width('100%')
.height('100%')
}
}
4.2 方法二:监听窗口尺寸变化
通过监听窗口尺寸变化判断横竖屏状态:
typescript
import { window } from '@kit.ArkUI';
@Entry
@Component
struct SizeChangePage {
@State isLandscape: boolean = false;
private windowStage: window.WindowStage =
AppStorage.get('windowStage') as window.WindowStage;
private mainWindow: window.Window = this.windowStage.getMainWindowSync();
aboutToAppear() {
// 注册窗口尺寸变化监听
this.mainWindow.on('windowSizeChange', (data: window.Size) => {
// 判断宽高比
const width = px2vp(data.width);
const height = px2vp(data.height);
this.isLandscape = width > height;
});
}
aboutToDisappear() {
this.mainWindow.off('windowSizeChange');
}
build() {
// 根据 isLandscape 渲染不同布局
}
}
4.3 方法三:在 Ability 层监听
在 UIAbility 中通过 onConfigurationChanged 监听配置变化:
typescript
import { UIAbility, Configuration } from '@kit.AbilityKit';
export default class EntryAbility extends UIAbility {
onConfigurationChanged(config: Configuration) {
// 检查方向变化
if (config.orientation === Configuration.Orientation.LANDSCAPE) {
console.log('切换到横屏');
} else if (config.orientation === Configuration.Orientation.PORTRAIT) {
console.log('切换到竖屏');
}
}
}
五、响应式布局适配
5.1 根据方向动态调整布局
typescript
@Entry
@Component
struct NewsListPage {
@State isLandscape: boolean = false;
private listener?: mediaquery.MediaQueryListener;
aboutToAppear() {
this.listener = mediaquery.matchMediaSync('(orientation: landscape)');
this.listener.on('change', (result) => {
this.isLandscape = result.matches;
});
}
aboutToDisappear() {
this.listener?.off('change');
}
build() {
Column() {
// 动态网格:横屏双列,竖屏单列
Grid() {
ForEach(this.newsData, (item: NewsItem) => {
GridItem() {
NewsCard({ news: item })
}
})
}
.columnsTemplate(this.isLandscape ? '1fr 1fr' : '1fr')
.rowsGap(10)
.columnsGap(10)
}
}
}
5.2 使用 GridContainer 实现断点适配
typescript
GridContainer() {
// 内容
}
.columnsTemplate(this.isLandscape ? '1fr 1fr 1fr' : '1fr 1fr')
六、多设备横竖屏适配策略
6.1 设备类型判断
不同设备需要不同的横竖屏策略:
typescript
import { deviceInfo } from '@kit.BasicServicesKit';
// 判断设备类型
if (deviceInfo.deviceType === 'tablet') {
// 平板:支持自动旋转
mainWindow.setPreferredOrientation(window.Orientation.AUTO_ROTATION_RESTRICTED);
} else {
// 手机:默认竖屏
mainWindow.setPreferredOrientation(window.Orientation.PORTRAIT);
}
6.2 基于窗口尺寸的策略
推荐使用窗口尺寸判断是否支持旋转:
typescript
const BREAKPOINT_MD = 600;
function getWindowSize(): window.Rect {
let windowRect = mainWindow.getWindowProperties().windowRect;
return windowRect;
}
function shouldSupportRotation(): boolean {
let rect = getWindowSize();
let widthVp = px2vp(rect.width);
let heightVp = px2vp(rect.height);
let aspectRatio = heightVp / widthVp;
// 条件1:窗口最小边 >= 600vp(大屏设备)
if (Math.min(widthVp, heightVp) >= BREAKPOINT_MD) {
return true;
}
// 条件2:类方屏(宽高比在 0.8~1.2 之间)
if (aspectRatio >= 0.8 && aspectRatio < 1.2) {
return true;
}
// 条件3:平板设备
if (deviceInfo.deviceType === 'tablet') {
return true;
}
return false; // 默认竖屏
}
6.3 完整的多设备适配方案
在 UIAbility 中实现完整的旋转策略:
typescript
import { UIAbility } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { deviceInfo } from '@kit.BasicServicesKit';
export default class EntryAbility extends UIAbility {
private windowObj?: window.Window;
setDefaultOrientation(): void {
const BREAKPOINT_MD = 600;
let windowRect = this.windowObj!.getWindowProperties().windowRect;
let windowWidthVp = px2vp(windowRect.width);
let windowHeightVp = px2vp(windowRect.height);
let aspectRatio = windowHeightVp / windowWidthVp;
// 判断是否支持旋转
if (
Math.min(windowWidthVp, windowHeightVp) >= BREAKPOINT_MD ||
(aspectRatio >= 0.8 && aspectRatio < 1.2) ||
deviceInfo.deviceType === 'tablet'
) {
// 支持自动旋转
this.windowObj?.setPreferredOrientation(
window.Orientation.AUTO_ROTATION_RESTRICTED
);
} else {
// 竖屏显示
this.windowObj?.setPreferredOrientation(
window.Orientation.PORTRAIT
);
}
}
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.getMainWindow().then((windowObj) => {
this.windowObj = windowObj;
this.setDefaultOrientation();
// 监听窗口尺寸变化
this.windowObj.on('windowSizeChange', () => {
this.setDefaultOrientation();
});
});
windowStage.loadContent('pages/Index');
}
}
七、注意事项与最佳实践
7.1 状态保存与恢复
横竖屏切换时,页面会重新渲染,需要使用 @State 保存状态:
typescript
@Entry
@Component
struct MyPage {
@State videoProgress: number = 0;
@State currentTab: number = 0;
// @State 装饰的变量在配置变更时会自动保留
}
7.2 性能优化
-
避免在
onConfigurationChanged中进行耗时操作 -
使用
@State精确控制需要更新的组件 -
大型列表使用 LazyForEach 实现懒加载
7.3 折叠屏特殊处理
折叠屏设备需要额外关注折叠态变化:
typescript
import { display } from '@kit.ArkUI';
// 检查是否为折叠屏
let isFoldable = display.isFoldable();
// 获取折叠状态
display.getFoldStatus((err, status) => {
if (status === display.FoldStatus.FOLD_STATUS_EXPANDED) {
// 展开态:支持横竖屏
} else if (status === display.FoldStatus.FOLD_STATUS_FOLDED) {
// 折叠态:类似手机
}
});
7.4 常见问题
Q1:页面固定了方向,但是切换页面后方向恢复?
A:方向设置是基于窗口的,需要在每个页面的 onPageShow 中重新设置方向。
Q2:监听方向变化不生效?
A:检查是否在 aboutToDisappear 中正确移除监听器,避免内存泄漏。
Q3:横竖屏切换时 UI 闪烁?
A:使用 @State 驱动布局变化,避免在 build 方法中使用复杂的条件判断。
八、总结
| 方法 | 适用场景 | 优先级 |
|---|---|---|
module.json5 静态配置 |
整个页面固定方向 | 最低 |
setPreferredOrientation() |
动态切换方向 | 推荐 |
mediaquery 监听 |
响应式布局 | 推荐 |
| 窗口尺寸监听 | 需要获取具体尺寸 | 备选 |
| Ability 配置变更 | 系统级监听 | 深度需求 |
HarmonyOS 5 提供了完整的横竖屏管理能力,从简单的固定方向到复杂的多设备自适应,开发者可以根据应用需求选择合适的方案。在多设备场景下,建议结合设备类型、窗口尺寸和宽高比综合判断是否支持旋转,以实现最佳的跨端体验。
更多推荐



所有评论(0)