HarmonyOS鸿蒙Next中用ArkTS开发时,如何监听屏幕旋转并动态调整布局?
HarmonyOS鸿蒙Next中用ArkTS开发时,如何监听屏幕旋转并动态调整布局? 我们应用中希望横屏时显示双人画中画,竖屏时切回单人全屏。 有推荐的响应式方案吗?
推荐使用 WindowStage + 响应式布局:
// 监听窗口尺寸变化
windowClass.on('sizeChange', (size) => {
if (size.width > size.height) {
// 横屏:启用双窗格布局
this.layoutMode = 'landscape';
} else {
// 竖屏:单窗格
this.layoutMode = 'portrait';
}
});
配合 @Builder 和 if/else 动态构建 UI,或使用 Flex + displayPriority 实现自适应。避免硬编码宽高,优先使用百分比或 Resource 中的断点配置。
更多关于HarmonyOS鸿蒙Next中用ArkTS开发时,如何监听屏幕旋转并动态调整布局?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
开发者您好,可以采用如下方案:
【解决方案】
-
方案一:监听屏幕旋转的能力可以通过display.on(‘change’)监听屏幕状态改变实现,并通过display.getDefaultDisplaySync()获取屏幕实例对象,其中rotation属性用于显示设备的屏幕顺时针旋转角度。实现示例如下:
import window from '[@ohos](/user/ohos).window'; import display from '[@ohos](/user/ohos).display'; import { common } from '@kit.AbilityKit'; const ORIENTATION: Array<string> = ['垂直', '水平', '反向垂直', '反向水平']; @Entry @Component struct Scene1 { @State rotation: number = 0; @State message: string = ORIENTATION[this.rotation]; // 是否横屏状态 @State @Watch('setWindowLayOut') isLandscape: boolean = false; aboutToAppear() { // 监听屏幕状态改变 display.on('change', async () => { // 获取当前旋转角度 this.rotation = display.getDefaultDisplaySync().rotation; this.message = ORIENTATION[this.rotation]; }); } setWindowLayOut() { // 调用该接口手动改变设备横竖屏状态(设置全屏模式,先强制横屏,再加上传感器模式) let context = this.getUIContext()?.getHostContext() as common.UIAbilityContext; window.getLastWindow(context).then((windowClass) => { if (this.isLandscape) { // 设置屏幕横屏 windowClass.setPreferredOrientation(window.Orientation.AUTO_ROTATION_LANDSCAPE); } else { // 设置屏幕竖屏 windowClass.setPreferredOrientation(window.Orientation.AUTO_ROTATION_PORTRAIT); } }); } build() { Row() { Column() { Text(`${this.rotation}`) .fontSize(25) Text(`${this.message}`) .fontSize(25) Button(this.isLandscape ? '竖屏' : '横屏') .width(140) .onClick(() => { // 设置横屏 this.isLandscape = !this.isLandscape; }); } .width('100%') } .height('100%') } } -
方案二:可在EntryAbility的onWindowStageCreate中注册windowsSizeChange事件,并通过AppStorage存储,在页面中通过StorageProp监听数据的变化实现屏幕旋转的监听,可参考以下示例:
- 在EntryAbility中注册监听窗口的windowsSizeChange事件,并使用StorageProp存储屏幕的显示方向:
import { UIAbility } from '@kit.AbilityKit'; import { hilog } from '@kit.PerformanceAnalysisKit'; import { display, window } from '@kit.ArkUI'; export default class EntryAbility extends UIAbility { onCreate(): void { hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); } onDestroy(): void { hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); } onWindowStageCreate(windowStage: window.WindowStage): void { hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); let mainWindow: window.Window; try { mainWindow = windowStage.getMainWindowSync(); let displayClass: display.Display = display.getDefaultDisplaySync(); AppStorage.setOrCreate('orientation', displayClass.orientation); // 监听窗口的windowsSizeChange事件,旋转屏时会触发该事件 mainWindow.on('windowSizeChange', () => { let displayClass: display.Display | null = null; try { displayClass = display.getDefaultDisplaySync(); // 获取屏幕的显示方向 AppStorage.set('orientation', displayClass.orientation); } catch { return; } }); } catch { hilog.info(0x0000, 'testTag', '%{public}s', 'error'); return; } //此处需要替换为需要展示的页面 windowStage.loadContent('pages/Scene2', (err) => { if (err.code) { return; } hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); }); } onWindowStageDestroy(): void { // Main window is destroyed,release UI related resources hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); } onForeground(): void { // Ability has brought to foreground hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); } onBackground(): void { // Ability has back to background hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); } } - 页面中通过StorageProp监听数据的变化实现屏幕旋转的监听:
import { display } from '@kit.ArkUI'; @Entry @Component struct Scene2 { // 获取通过监听窗口的windowsSizeChange事件得到的屏幕显示方向 @StorageProp('orientation') myOrientation: display.Orientation = 0; build() { Stack() { // 当屏幕显示方向变化时,切换组件的视图效果 if (this.myOrientation === 0 || this.myOrientation === 2) { Image($r('app.media.startIcon')) .size({ width: 100, height: 100 }) .id('image1') } else { Image($r('app.media.background')) .position({ x: 0, y: 0 }) .size({ width: 200, height: 200 }) .id('image2') } } .backgroundColor(Color.White) .size({ width: '100%', height: '100%' }) } } - 需要在项目的module.json5文件中的abilities列表里添加"orientation",指定为"auto_rotation":
"orientation": "auto_rotation",
- 在EntryAbility中注册监听窗口的windowsSizeChange事件,并使用StorageProp存储屏幕的显示方向:
-
方案三:通过设备中的重力传感器,可以检测该设备在三个坐标轴(即X轴、Y轴及Z轴)上的线性加速度。基于这些数据,能够计算出屏幕当前的旋转状态。有关具体实现方式,可参考以下样例:
import sensor from '[@ohos](/user/ohos).sensor'; import base from '[@ohos](/user/ohos).base'; @Entry @Component struct Scene3 { @State rotation: string = 'INVALID'; onDegree(callback: base.Callback<string>): void { sensor.on(sensor.SensorId.GRAVITY, (data: sensor.GravityResponse) => { let degree: number = -1; degree = this.CalDegree(data.x, data.y, data.z); if (degree >= 0 && (degree <= 30 || degree >= 330)) { this.rotation = 'ROTATION_0'; } else if (degree >= 60 && degree <= 120) { this.rotation = 'ROTATION_90'; } else if (degree >= 150 && degree <= 210) { this.rotation = 'ROTATION_180'; } else if (degree >= 240 && degree <= 300) { this.rotation = 'ROTATION_270'; } callback(this.rotation); }); } CalDegree(x: number, y: number, z: number): number { let degree: number = -1; // 3为有效_增量_角度_阈值_系数 if ((x * x + y * y) * 3 < z * z) { return degree; } degree = 90 - (Number)(Math.round(Math.atan2(y, -x) / Math.PI * 180)); return degree >= 0 ? degree % 360 : degree % 360 + 360; } aboutToAppear() { let callback = async (rotation: string) => { console.info(`rotation = ${rotation}`); }; try { this.onDegree(callback); } catch (exception) { } } build() { Row() { Column() { Text(`${this.rotation}`) .fontSize(25) } .width('100%') } .height('100%') } } -
方案四:使用媒体查询接口监听屏幕旋转,设置媒体查询的查询条件为屏幕的方向,可选值为orientation:portrait(设备竖屏)和orientation: landscape(设备横屏)。有关具体实现方式,请参考:[媒体查询 (@ohos.mediaquery)场景示例](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-layout-development-media-query#场景示例)。
【总结】
四种监听屏幕旋转事件方案的对比:
| 方案 | 优缺点 | 适用场景 |
|---|---|---|
| 方案一:display.on(‘change’) | 使用便捷,但受module.json5配置信息的影响。 | 1. “orientation"指定为"auto_rotation”。 2. 适用于单个页面的旋转事件监听。 |
| 方案二:windowsSizeChange | 生命周期内将持续保持监听状态,旋转数据需进行传递,且受module.json5配置信息的影响 | 1. “orientation"指定为"auto_rotation” 。 2. 适用于全局多个页面频繁旋转的监听机制。 |
| 方案三:sensor.SensorId.GRAVITY | 不受module.json5配置信息的影响,传感器数据需要进行转换,目前不支持模拟器使用。 | 1. “orientation"指定为非"auto_rotation”。 2. 当前页面无需根据传感器的变动进行旋转,但仍需响应屏幕旋转而执行相应操作的业务场景。 |
| 方案四:this.getUIContext().getMediaQuery().matchMediaSync() | 该方案的使用较为便捷,然而其可用性受到module.json5配置文件信息的制约,不可在UIAbility的生命周期管理过程中直接调用,而需在完成组件实例的创建之后方可启用。此外,该方案当前无法识别屏幕旋转的横竖屏模式是正向还是反向。 | 1. “orientation"指定为"auto_rotation”。 2. 只需监听屏幕方向的横竖变化,无需关注横竖方向的具体正反向情况的业务场景。 |
除可在module.json5文件中全局配置当前UIAbility组件启动时的方向之外,亦可通过调用window.setPreferredOrientation方法动态设定特定页面的窗口方向,请根据具体的业务场景选择合适的方案来监听屏幕旋转事件。
在应用程序开发中,应当如何设置以检测并响应设备屏幕旋转的事件?
方案一:监听屏幕旋转的能力可以通过display.on(‘change’)监听屏幕状态改变实现,并通过display.getDefaultDisplaySync()获取屏幕实例对象,其中rotation属性用于显示设备的屏幕顺时针旋转角度。
import window from '@ohos.window';
import display from '@ohos.display';
import { common } from '@kit.AbilityKit';
const ORIENTATION: Array<string> = ['垂直', '水平', '反向垂直', '反向水平'];
@Entry
@Component
struct Scene1 {
@State rotation: number = 0;
@State message: string = ORIENTATION[this.rotation];
// 是否横屏状态
@State @Watch('setWindowLayOut') isLandscape: boolean = false;
aboutToAppear() {
// 监听屏幕状态改变
display.on('change', async () => {
// 获取当前旋转角度
this.rotation = display.getDefaultDisplaySync().rotation;
this.message = ORIENTATION[this.rotation];
});
}
setWindowLayOut() {
// 调用该接口手动改变设备横竖屏状态(设置全屏模式,先强制横屏,再加上传感器模式)
let context = this.getUIContext()?.getHostContext() as common.UIAbilityContext;
window.getLastWindow(context).then((windowClass) => {
if (this.isLandscape) {
// 设置屏幕横屏
windowClass.setPreferredOrientation(window.Orientation.AUTO_ROTATION_LANDSCAPE);
} else {
// 设置屏幕竖屏
windowClass.setPreferredOrientation(window.Orientation.AUTO_ROTATION_PORTRAIT);
}
});
}
build() {
Row() {
Column() {
Text(`${this.rotation}`)
.fontSize(25)
Text(`${this.message}`)
.fontSize(25)
Button(this.isLandscape ? '竖屏' : '横屏')
.width(140)
.onClick(() => {
// 设置横屏
this.isLandscape = !this.isLandscape;
});
}
.width('100%')
}
.height('100%')
}
}
我也想知道
我也想知道
在ArkTS中,使用window.getLastWindow()获取窗口对象,通过on('windowSizeChange')监听窗口尺寸变化事件,这包含了屏幕旋转。在回调函数中,可以获取新的窗口宽度和高度,据此判断横竖屏状态,并动态更新@State修饰的布局变量,UI会自动刷新。
在HarmonyOS Next中,可以使用display模块的on('orientationChange')接口监听屏幕旋转事件,并结合响应式布局方案动态调整UI。以下是实现方案:
-
监听屏幕方向变化:
import display from '[@ohos](/user/ohos).display'; // 监听屏幕旋转 display.on('orientationChange', (curOrientation) => { if (curOrientation === display.Orientation.VERTICAL) { // 竖屏:单人全屏布局 this.mediaLayout = LayoutType.SINGLE_FULL; } else if (curOrientation === display.Orientation.HORIZONTAL) { // 横屏:双人画中画布局 this.mediaLayout = LayoutType.DUAL_PIP; } // 触发UI更新 }); -
响应式布局实现:
- 使用
Flex或Grid容器配合mediaQuery实现自适应 - 通过状态变量控制布局切换:
[@Component](/user/Component) struct MediaContainer { @State layoutType: LayoutType = LayoutType.SINGLE_FULL; build() { Flex({ direction: this.layoutType === LayoutType.SINGLE_FULL ? FlexDirection.Column : FlexDirection.Row }) { // 主画面 MediaPlayer() .width(this.layoutType === LayoutType.SINGLE_FULL ? '100%' : '70%') // 画中画(横屏时显示) if (this.layoutType === LayoutType.DUAL_PIP) { MediaPlayer() .width('30%') .position({ x: '70%', y: 0 }) } } } } - 使用
-
优化建议:
- 在
aboutToAppear中获取初始方向:display.getDefaultDisplaySync().orientation - 使用
mediaQuery设置断点:(orientation: landscape)和(orientation: portrait) - 通过
window.getWindowProperties().orientation兼容API 12+版本
- 在
此方案通过监听系统方向变化触发状态更新,利用ArkTS声明式UI自动重组布局,实现横竖屏的无缝切换。

