问答帖原创发布在华为开发者联盟社区 ,欢迎开发者前往论坛提问交流。

更多与该问题相关的讨论,请点击原帖查看:

window.setMainWindowOrientation 设置横竖屏切换,为什么没有切换完成后的监听?-华为开发者问答 | 华为开发者联盟 (huawei.com)

问题描述:

window.setMainWindowOrientation 设置横竖屏切换,为什么没有切换完成后的监听?

解决方案:

【背景知识】

@ohos.display (屏幕属性)是用于给予开发者获取显示设备分辨率、旋转角度等信息的能力。

on('windowSizeChange')开启窗口尺寸变化的监听。

@ohos.sensor (传感器)模块提供了获取传感器数据的能力,用于收集设备内外环境的各种数据。GRAVITY重力传感器能够帮助设备感知其在空间中的位置变化和运动状态。

@ohos.mediaquery (媒体查询)可以设置媒体查询的查询条件,并返回对应的监听句柄。

module.json5配置文件可以配置orientation标识当前UIAbility组件启动时的方向。

【解决方案】

实现监听屏幕旋转的能力可以通过display.on("change")监听屏幕状态改变,并通过display.getDefaultDisplaySync()获取屏幕示例对象,其中rotation属性用于显示设备的屏幕顺时针旋转角度。实现示例如下:

import window from '@ohos.window';

import display from '@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');
    }
  }
  
  ```
  - 页面中通过StorageLink监听数据的变换实现屏幕旋转的监听:
  ```ts
  // 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%' })
    }
  }

通过设备中的重力传感器,可以检测该设备在三个坐标轴(即X轴、Y轴及Z轴)上的线性加速度。基于这些数据,能够推算出屏幕当前的旋转状态。有关具体实现方式,可参考以下示例: 

import sensor from '@ohos.sensor';

  import base from '@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方法动态设定特定页面的窗口方向,请根据具体的业务场景选择合适的方案来监听屏幕旋转事件。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐