HarmonyOS鸿蒙Next中window.setMainWindowOrientation设置横竖屏切换,为什么没有切换完成后的监听?不然页面处理不流程,比如我视屏横屏后,需要修改页面控件的布局,就很突兀

HarmonyOS鸿蒙Next中window.setMainWindowOrientation设置横竖屏切换,为什么没有切换完成后的监听?不然页面处理不流程,比如我视屏横屏后,需要修改页面控件的布局,就很突兀 横竖屏切换后,怎么监听切换完成?api12

在api19 看到新出来了一个监听,当时目前在api12上可以拿到window横竖屏切换完成的状态吗,不然ui组件的修改很突兀

3 回复

【背景知识】

【解决方案】

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确实没有提供直接的切换完成监听回调。目前可以通过监听屏幕方向变化事件来实现类似功能:

  1. 使用window.on('orientationChange')全局监听屏幕方向变化
  2. 在回调中判断当前方向与目标方向是否一致
  3. 方向一致时执行布局调整逻辑

示例代码:

window.on('orientationChange', (newOrientation) => {
  if(newOrientation === targetOrientation){
    // 执行布局调整
  }
});

这种方案可以避免布局调整的突兀感。

在HarmonyOS Next API12中,确实没有提供直接的横竖屏切换完成监听接口。针对这个问题,可以通过以下两种方式实现监听效果:

  1. 使用窗口尺寸变化监听替代:
window.on('windowSizeChange', (data) => {
  // 根据当前窗口宽高判断方向
  if (data.width > data.height) {
    // 横屏布局处理
  } else {
    // 竖屏布局处理
  }
});
  1. 结合系统配置变化监听:
app.on('configChange', (newConfig) => {
  if (newConfig.screenDirection === window.ScreenDirection.VERTICAL) {
    // 竖屏处理
  } else if (newConfig.screenDirection === window.ScreenDirection.HORIZONTAL) {
    // 横屏处理
  }
});

这两种方式都可以在API12上实现横竖屏切换后的UI适配,建议优先使用windowSizeChange方案,因为它的响应更及时。

回到顶部