HarmonyOs开发--设置屏幕朝向 orientation (横竖屏场景)
前言:
最近做了一个HarmonyOs的游戏应用涉及屏幕的朝向问题,发现很多不明白的地方,这里对屏幕朝向做个总结.希望其他开发者可以得到启发.
配置方式:
HarmonyOS应用在设置屏幕朝向(横竖屏时),主要有两种方式静态配置(全局默认)和动态代码控制(运行时手动切换),场景覆盖主要固定方向、自动旋转、页面的切换等场景的使用。
一、静态配置:应用默认方向(应用级配置)
配置地方:entry/src/main/module.json5
使用场景:设置应用启动固定方向
{ "module": { "abilities": [ { "name": "EntryAbility", // 主要配置 "orientation": "portrait", } ] } }orientation 可选值一共有18个(可参考下面官方链接文档):
Orientation取值:https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-window-e#orientation9
主要常用的可选值:
portrait:固定竖屏(手机应用最常用)landscape:固定横屏(主要用于游戏)auto_rotation:自动旋转(跟随传感器,不受系统锁定影响)auto_rotation_restricted:受系统锁定控制(用户开 “自动旋转” 才转)follow_desktop:跟随设备类型(平板 / 手机自动适配)unspecified:未指定(由系统决定)
选值建议:
若应用在直板机和双折叠折叠态是竖屏应用,平板和双折叠展开态是可旋转应用,推荐配置FOLLOW_DESKTOP为默认旋转策略。
若应用为竖屏应用,建议配置PORTRAIT为默认旋转策略。
若应用为横屏应用(如MOBA类游戏),启动时默认为横屏,存在以下两种情况:
仅支持横屏时,建议配置LANDSCAPE为默认旋转策略;
支持横屏和反向横屏切换时,建议配置AUTO_ROTATION_LANDSCAPE或AUTO_ROTATION_LANDSCAPE_RESTRICTED(是否受控制中心旋转开关控制)。
若应用为可旋转应用,建议配置AUTO_ROTATION_RESTRICTED为默认旋转策略。
二、动态代码控制:运行时切换(代码控制,即窗口级配置)
核心接口:
- 获取窗口:
window.getLastWindow(context)或windowStage.getMainWindowSync() - 设置方向:
setPreferredOrientation(orientation) - 方向枚举:
window.Orientation
(1)全局固定方向(EntryAbility.ets)
应用启动时强制锁定方向:
import { UIAbility } from '@kit.AbilityKit'; import { window } from '@kit.ArkUI'; export default class EntryAbility extends UIAbility { onWindowStageCreate(windowStage: window.WindowStage): void { // 1. 获取主窗口 const mainWindow = windowStage.getMainWindowSync(); // 2. 强制固定为竖屏 mainWindow.setPreferredOrientation(window.Orientation.PORTRAIT) .catch((err) => { console.error('设置方向失败:', err); }); // 加载页面 windowStage.loadContent('pages/Index'); } }(2)页面级切换
A 页面竖屏 → B 页面横屏 → 返回 A 恢复竖屏:
// VideoPage.ets(横屏页面) import { window } from '@kit.ArkUI'; import { common } from '@kit.AbilityKit'; @Entry @Component struct VideoPage { private context = getContext(this) as common.UIAbilityContext; private lastOrientation?: window.Orientation; // 保存原方向 // 页面显示时 → 切横屏 async aboutToAppear() { const win = await window.getLastWindow(this.context); // 保存当前方向 this.lastOrientation = win.getPreferredOrientation(); // 强制横屏 await win.setPreferredOrientation(window.Orientation.LANDSCAPE); } // 页面消失时 → 恢复原方向 async aboutToDisappear() { const win = await window.getLastWindow(this.context); if (this.lastOrientation) { await win.setPreferredOrientation(this.lastOrientation); } } build() { Column() { Text('视频页(强制横屏)') .fontSize(30) } .width('100%') .height('100%') .justifyContent(FlexAlign.Center) } }(3)按钮手动切换横竖屏
@Entry @Component struct OrientationDemo { private context = getContext(this) as common.UIAbilityContext; // 切换为竖屏 async setPortrait() { const win = await window.getLastWindow(this.context); await win.setPreferredOrientation(window.Orientation.PORTRAIT); } // 切换为横屏 async setLandscape() { const win = await window.getLastWindow(this.context); await win.setPreferredOrientation(window.Orientation.LANDSCAPE); } // 开启自动旋转 async setAutoRotation() { const win = await window.getLastWindow(this.context); await win.setPreferredOrientation(window.Orientation.AUTO_ROTATION); } build() { Column({ space: 20 }) { Button('固定竖屏') .onClick(() => this.setPortrait()); Button('固定横屏') .onClick(() => this.setLandscape()); Button('自动旋转') .onClick(() => this.setAutoRotation()); } .width('100%') .height('100%') .justifyContent(FlexAlign.Center); } }三、其他旋转配置
应用子窗口的旋转:
在应用旋转场景中,应用主窗的尺寸由系统控制,而应用子窗的尺寸和位置由应用控制。因此,建议应用开发者在有应用子窗的旋转场景中,同步调整应用子窗的尺寸和位置,避免因旋转过程中应用子窗的尺寸和位置保持不变而导致如下图所示的应用子窗显示截断问题(直板机默认的旋转策略为UNSPECIFIED,旋转锁定按钮关闭的情况下不允许应用旋转,可以通过module.json5配置文件中abilities标签的"orientation"字段配置应用的旋转策略为AUTO_ROTATION,使应用跟随设备方向旋转)。
实现方案
系统为设备窗口尺寸变化监听、设置应用子窗尺寸和位置提供了如下接口:
- on('windowSizeChange')接口用于开启窗口尺寸变化的监听,当窗口发生旋转后,会触发其中的回调。
- resize()接口用于改变当前窗口的大小,可以在窗口发生旋转后及时调整子窗的宽高。
- moveWindowTo()接口用于移动窗口位置,可以在窗口发生旋转后及时调整子窗的位置。
为实现根据应用旋转方向设置应用子窗尺寸,开发者可使用on('windowSizeChange')接口监听窗口尺寸的变化,并在回调函数中通过resize()接口和moveWindowTo()接口分别调整应用子窗的尺寸和位置。
需要指出的是,开发者可以使用setFollowParentWindowLayoutEnabled()接口设置子窗或模态窗口的布局信息是否跟随主窗,如果设置为跟随主窗,那么子窗的旋转便不再需要额外适配。
import { window } from '@kit.ArkUI'; import { BusinessError } from '@kit.BasicServicesKit'; import { hilog } from '@kit.PerformanceAnalysisKit'; const SUB_WINDOW_LEFT_OFFSET: number = 50; const SUB_WINDOW_TOP_OFFSET: number = 500; const TAG: string = 'subWindowAdaptWhenRotate'; const DOMAIN: number = 0x0000; @Entry @Component struct Index { public mainWindow: window.Window | undefined = undefined; public subWindow: window.Window | undefined = undefined; aboutToAppear(): void { // create subWindow this.createSubWindow(); this.mainWindow = AppStorage.get('mainWindow'); if (!this.mainWindow) { return; } this.mainWindow.on('windowSizeChange', () => { this.adjustSubwindowSizeAndPosition(); }) } private adjustSubwindowSizeAndPosition(): void { if (!this.subWindow) { hilog.error(DOMAIN, TAG, 'subWindow is null'); return; } let subwindowRect: window.Rect | null = null; try { subwindowRect = this.subWindow.getWindowProperties().windowRect; } catch (error) { hilog.warn(0x000, 'testTag', `getWindowProperties failed, code: ${error.code}, message: ${error.message}`); } let newWidth: number = subwindowRect!.height; let newHeight: number = subwindowRect!.width; let newX: number = subwindowRect!.top; let newY: number = subwindowRect!.left; this.subWindow.resize(newWidth, newHeight) .then(() => { hilog.info(DOMAIN, TAG, 'Succeeded in changing the window size') }).catch((err: BusinessError) => { hilog.error(DOMAIN, TAG, `Failed to change the window size. Cause code: ${err.code}, message: ${err.message}`); }); this.subWindow.moveWindowTo(newX, newY) .then(() => { hilog.info(DOMAIN, TAG, 'Succeeded in moving the window'); }).catch((err: BusinessError) => { hilog.error(DOMAIN, TAG, `Failed to move the window. Cause code: ${err.code}, message: ${err.message}`); }); } // ... }悬浮窗的旋转:
悬浮窗默认是竖向的,但是对于横向游戏和视频应用,横向的悬浮窗体验会更好。开发者可以通过在module.json5配置文件中abilities标签下的preferMultiWindowOrientation属性增加“landscape”或者“landscape_auto”,配合API以声明应用支持横向悬浮窗或上下分屏模式。
{ "module": { // ... "abilities": [ { "name": "EntryAbility", // ... "preferMultiWindowOrientation": "landscape_auto", // ... } ], // ... } }该场景下多窗布局动态可变为横向,需要配合API(enableLandscapeMultiWindow()/ disableLandscapeMultiWindow())使用。
private windowClass = (this.getUIContext().getHostContext() as common.UIAbilityContext).windowStage.getMainWindowSync() aboutToAppear(): void { this.windowClass.enableLandscapeMultiWindow(); } aboutToDisappear(): void { this.windowClass.disableLandscapeMultiWindow(); }实现效果:
附:
文章部分参考wiki:
设置屏幕朝向 orientation (横竖屏) | 华为开发者联盟
