HarmonyOS鸿蒙Next中window.setMainWindowOrientation设置横竖屏切换,为什么没有切换完成后的监听?不然页面处理不流程,比如我视屏横屏后,需要修改页面控件的布局,就很突兀
HarmonyOS鸿蒙Next中window.setMainWindowOrientation设置横竖屏切换,为什么没有切换完成后的监听?不然页面处理不流程,比如我视屏横屏后,需要修改页面控件的布局,就很突兀 横竖屏切换后,怎么监听切换完成?api12
在api19 看到新出来了一个监听,当时目前在api12上可以拿到window横竖屏切换完成的状态吗,不然ui组件的修改很突兀
【背景知识】
- [@ohos.display (屏幕属性)](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-display) 是用于给予开发者获取显示设备分辨率、旋转角度等信息的能力。
- on(‘windowSizeChange’) 开启窗口尺寸变化的监听。
- [@ohos.sensor (传感器)](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-sensor) 模块提供了获取传感器数据的能力,用于收集设备内外环境的各种数据。GRAVITY重力传感器能够帮助设备感知其在空间中的位置变化和运动状态。
- [@ohos.mediaquery (媒体查询)](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-mediaquery#mediaquerymatchmediasync) 可以设置媒体查询的查询条件,并返回对应的监听句柄。
- module.json5配置文件 可以配置orientation标识当前UIAbility组件启动时的方向。
【解决方案】
- 实现监听屏幕旋转的能力可以通过 display.on(“change”) 监听屏幕状态改变,并通过 display.getDefaultDisplaySync() 获取屏幕示例对象,其中rotation属性用于显示设备的屏幕顺时针旋转角度。实现示例如下:
import window from '[@ohos](/user/ohos).window';
import display from '[@ohos](/user/ohos).display';
const ORIENTATION: Array<string> = ['垂直', '平', '反向垂直', '反向水平']
@Entry
@Component
struct OrientationPage {
@State rotation: number = 0
@State message: string = ORIENTATION[this.rotation]
// 是否横屏状态
@State @Watch('setWindowLayOut') isLandscape: boolean = false;
aboutToAppear() {
// 监听屏幕状态改变
display.on("change", async () => {
// 获取当前旋转角度
this.rotation = await display.getDefaultDisplaySync().rotation
this.message = ORIENTATION[this.rotation]
});
}
setWindowLayOut() {
// 调用该接口手动改变设备横竖屏状态(设置全屏模式,先强制横屏,再加上传感器模式)
window.getLastWindow(getContext(this)).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 存储,在页面中通过StorageLink监听数据的变换实现屏幕旋转的监听,可参考以下示例:
// EntryAbility.ets
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { display, window } from '@kit.ArkUI';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): 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', (data) => {
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/Index', (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');
}
}
// index.ets
import { display } from '@kit.ArkUI';
@Entry
@Component
struct Index {
// 获取通过监听窗口的windowsSizeChange事件得到的屏幕显示方向
@StorageLink('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":
{
"module": {
// ...
"abilities": [
{
// ...
"orientation": "auto_rotation",
}
],
}
}
- 通过设备中的重力传感器,可以检测该设备在三个坐标轴(即X轴、Y轴及Z轴)上的线性加速度。基于这些数据,能够推算出屏幕当前的旋转状态。有关具体实现方式,可参考以下示例:
import sensor from '[@ohos](/user/ohos).sensor';
import base from '[@ohos](/user/ohos).base';
@Entry
@Component
struct OrientationPage {
@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)
console.log(degree + "zzz")
if (degree >= 0 && (degree <= 30 || degree >= 330)) {
this.rotation = "ROTATION_0";
} else if (degree >= 60 && degree <= 120) { // Use ROTATION_90 when degree range is [60, 120]
this.rotation = "ROTATION_90";
} else if (degree >= 150 && degree <= 210) { // Use ROTATION_180 when degree range is [150, 210]
this.rotation = "ROTATION_180";
} else if (degree >= 240 && degree <= 300) { // Use ROTATION_270 when degree range is [240, 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.log('rotation = ' + rotation)
}
try {
this.onDegree(callback);
} catch (exception) {
}
}
build() {
Row() {
Column() {
Text(`${this.rotation}`)
.fontSize(25)
}
.width("100%")
}
.height('100%')
}
}
- 使用媒体查询接口监听屏幕旋转,设置媒体查询的查询条件为屏幕的方向,可选值为orientation:portrait(设备竖屏)和orientation: landscape(设备横屏)。有关具体实现方式,可参考以下示例:
import { mediaquery } from '@kit.ArkUI';
// 监听横屏事件
let listener = mediaquery.matchMediaSync('(orientation: portrait)');
function onPortrait(mediaQueryResult: mediaquery.MediaQueryResult) {
if (mediaQueryResult.matches) {
console.log(`横屏`)
} else {
}
}
// 注册回调
listener.on('change', onPortrait)
// 取消注册回调
listener.off('change', onPortrait)
【总结】
除可在module.json5文件中全局配置当前UIAbility组件启动时的方向之外,亦可通过调用window.setPreferredOrientation方法动态设定特定页面的窗口方向,请根据具体的业务场景选择合适的方案来监听屏幕旋转事件。
更多关于HarmonyOS鸿蒙Next中window.setMainWindowOrientation设置横竖屏切换,为什么没有切换完成后的监听?不然页面处理不流程,比如我视屏横屏后,需要修改页面控件的布局,就很突兀的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS Next中,window.setMainWindowOrientation
确实没有提供直接的切换完成监听回调。目前可以通过监听屏幕方向变化事件来实现类似功能:
- 使用
window.on('orientationChange')
全局监听屏幕方向变化 - 在回调中判断当前方向与目标方向是否一致
- 方向一致时执行布局调整逻辑
示例代码:
window.on('orientationChange', (newOrientation) => {
if(newOrientation === targetOrientation){
// 执行布局调整
}
});
这种方案可以避免布局调整的突兀感。
在HarmonyOS Next API12中,确实没有提供直接的横竖屏切换完成监听接口。针对这个问题,可以通过以下两种方式实现监听效果:
- 使用窗口尺寸变化监听替代:
window.on('windowSizeChange', (data) => {
// 根据当前窗口宽高判断方向
if (data.width > data.height) {
// 横屏布局处理
} else {
// 竖屏布局处理
}
});
- 结合系统配置变化监听:
app.on('configChange', (newConfig) => {
if (newConfig.screenDirection === window.ScreenDirection.VERTICAL) {
// 竖屏处理
} else if (newConfig.screenDirection === window.ScreenDirection.HORIZONTAL) {
// 横屏处理
}
});
这两种方式都可以在API12上实现横竖屏切换后的UI适配,建议优先使用windowSizeChange方案,因为它的响应更及时。