鸿蒙6开发视频播放器的屏幕方向适配问题

大家好,我是 V 哥, 在鸿蒙6开发中,屏幕方向适配是提升用户体验的重要环节。下面我将通过一个完整的视频播放器示例,详细讲解ArkTS中横竖屏切换的实现方案。

联系V哥获取 鸿蒙学习资料

一、基础概念理解

1.1 屏幕方向类型

鸿蒙系统支持四种屏幕方向:

  • PORTRAIT(竖屏):屏幕高度大于宽度
  • LANDSCAPE(横屏):屏幕宽度大于高度
  • PORTRAIT_INVERTED(反向竖屏)
  • LANDSCAPE_INVERTED(反向横屏)

1.2 适配策略

  • 静态配置:通过配置文件锁定基础方向
  • 动态调整:运行时感知设备旋转并智能适配

二、静态配置实现

2.1 修改module.json5配置

src/main/module.json5文件中配置UIAbility的方向属性:

json 复制代码
{
  "module": {
    "abilities": [
      {
        "name": "EntryAbility",
        "orientation": "landscape", // 可选:portrait|landscape|unspecified
        "metadata": [
          {
            "name": "ohos.ability.orientation",
            "value": "$profile:orientation"
          }
        ]
      }
    ]
  }
}

参数说明

  • portrait:锁定竖屏
  • landscape:锁定横屏
  • unspecified:跟随系统(默认)

三、动态横竖屏切换实现

3.1 创建方向工具类

新建utils/OrientationUtil.ets文件:

typescript 复制代码
// OrientationUtil.ets
import window from '@ohos.window';
import display from '@ohos.display';

export class OrientationUtil {
  // 设置窗口方向
  static async setPreferredOrientation(windowClass: window.Window, orientation: window.Orientation) {
    try {
      await windowClass.setPreferredOrientation(orientation);
      console.info('屏幕方向设置成功:', orientation);
    } catch (error) {
      console.error('设置屏幕方向失败:', error);
    }
  }

  // 获取当前设备方向
  static getCurrentOrientation(): string {
    const displayInfo = display.getDefaultDisplaySync();
    return displayInfo.width > displayInfo.height ? 'landscape' : 'portrait';
  }

  // 横屏模式配置
  static readonly LANDSCAPE: window.Orientation = window.Orientation.LANDSCAPE;
  
  // 竖屏模式配置  
  static readonly PORTRAIT: window.Orientation = window.Orientation.PORTRAIT;
  
  // 跟随传感器自动旋转(受旋转锁控制)
  static readonly FOLLOW_SENSOR: window.Orientation = window.Orientation.FOLLOW_SENSOR;
}

3.2 视频播放页面实现

创建pages/VideoPlayback.ets主页面:

typescript 复制代码
// VideoPlayback.ets
import { OrientationUtil } from '../utils/OrientationUtil';
import window from '@ohos.window';
import common from '@ohos.app.ability.common';
import mediaquery from '@ohos.mediaquery';

@Entry
@Component
struct VideoPlayback {
  @State currentOrientation: string = 'portrait';
  @State isFullScreen: boolean = false;
  private context: common.UIContext = getContext(this) as common.UIContext;
  private windowClass: window.Window | null = null;
  private mediaQueryListener: mediaquery.MediaQueryListener | null = null;

  // 页面初始化
  aboutToAppear() {
    this.initWindow();
    this.setupOrientationListener();
  }

  // 初始化窗口
  async initWindow() {
    try {
      this.windowClass = await window.getLastWindow(this.context);
      AppStorage.setOrCreate('windowClass', this.windowClass);
      this.currentOrientation = OrientationUtil.getCurrentOrientation();
    } catch (error) {
      console.error('窗口初始化失败:', error);
    }
  }

  // 设置方向监听器
  setupOrientationListener() {
    // 监听窗口尺寸变化
    this.windowClass?.on('windowSizeChange', () => {
      this.currentOrientation = OrientationUtil.getCurrentOrientation();
      console.info('屏幕方向变化:', this.currentOrientation);
    });

    // 媒体查询监听横屏事件
    const mediaQuery = mediaquery.matchMediaSync('(orientation: landscape)');
    this.mediaQueryListener = mediaQuery;
    mediaQuery.on('change', (result: mediaquery.MediaQueryResult) => {
      if (result.matches) {
        console.info('当前为横屏模式');
      } else {
        console.info('当前为竖屏模式');
      }
    });
  }

  // 切换全屏/竖屏模式
  async toggleFullScreen() {
    if (!this.windowClass) return;

    if (this.isFullScreen) {
      // 退出全屏,切换回竖屏
      await OrientationUtil.setPreferredOrientation(this.windowClass, OrientationUtil.PORTRAIT);
      this.isFullScreen = false;
    } else {
      // 进入全屏,切换为横屏并跟随传感器
      await OrientationUtil.setPreferredOrientation(this.windowClass, OrientationUtil.FOLLOW_SENSOR);
      this.isFullScreen = true;
    }
  }

  // 锁定横屏(不受传感器影响)
  async lockLandscape() {
    if (this.windowClass) {
      await OrientationUtil.setPreferredOrientation(this.windowClass, OrientationUtil.LANDSCAPE);
    }
  }

  // 页面布局
  build() {
    Column() {
      // 标题栏
      Row() {
        Text('视频播放器')
          .fontSize(20)
          .fontColor(Color.White)
      }
      .width('100%')
      .height(60)
      .backgroundColor('#007DFF')
      .justifyContent(FlexAlign.Start)
      .padding({ left: 20 })

      // 视频播放区域
      Column() {
        if (this.currentOrientation === 'landscape') {
          this.LandscapeVideoContent()
        } else {
          this.PortraitVideoContent()
        }
      }
      .layoutWeight(1)

      // 控制按钮区域(竖屏时显示)
      if (this.currentOrientation === 'portrait') {
        Column() {
          Button(this.isFullScreen ? '退出全屏' : '进入全屏')
            .width('90%')
            .height(40)
            .backgroundColor('#007DFF')
            .fontColor(Color.White)
            .onClick(() => this.toggleFullScreen())

          Button('锁定横屏')
            .width('90%')
            .height(40)
            .margin({ top: 10 })
            .backgroundColor('#FF6A00')
            .fontColor(Color.White)
            .onClick(() => this.lockLandscape())
        }
        .width('100%')
        .padding(10)
      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F5F5')
  }

  // 横屏内容布局
  @Builder LandscapeVideoContent() {
    Column() {
      // 模拟视频播放器
      Stack() {
        Image($r('app.media.video_poster'))
          .width('100%')
          .height(300)
          .objectFit(ImageFit.Contain)

        // 横屏控制条
        Row() {
          Button('退出全屏')
            .width(100)
            .height(30)
            .backgroundColor(Color.Orange)
            .fontColor(Color.White)
            .onClick(() => this.toggleFullScreen())
        }
        .width('100%')
        .justifyContent(FlexAlign.End)
        .padding(10)
      }

      // 视频信息
      Text('当前模式:横屏全屏播放')
        .fontSize(16)
        .margin({ top: 20 })
    }
  }

  // 竖屏内容布局
  @Builder PortraitVideoContent() {
    Column() {
      // 模拟视频播放器
      Image($r('app.media.video_poster'))
        .width('100%')
        .height(200)
        .objectFit(ImageFit.Cover)

      // 视频信息
      Text('视频标题:鸿蒙开发教程')
        .fontSize(18)
        .margin({ top: 10 })

      Text('视频描述:学习ArkTS横竖屏适配')
        .fontSize(14)
        .margin({ top: 5 })
        .fontColor(Color.Gray)
    }
    .padding(10)
  }

  // 页面销毁
  aboutToDisappear() {
    this.mediaQueryListener?.off('change');
    this.windowClass?.off('windowSizeChange');
  }
}

3.3 EntryAbility配置

更新entryability/EntryAbility.ets

typescript 复制代码
// EntryAbility.ets
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';
import hilog from '@ohos.hilog';

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
    hilog.info(0x0000, 'EntryAbility', 'Ability onCreate');
  }

  onWindowStageCreate(windowStage: window.WindowStage) {
    hilog.info(0x0000, 'EntryAbility', 'Ability onWindowStageCreate');

    // 设置窗口方向为跟随传感器
    windowStage.getMainWindow((err, windowClass) => {
      if (err) {
        hilog.error(0x0000, 'EntryAbility', 'Failed to get main window');
        return;
      }
      // 初始设置为竖屏,但允许跟随传感器旋转
      windowClass.setPreferredOrientation(window.Orientation.FOLLOW_SENSOR);
    });

    windowStage.loadContent('pages/VideoPlayback', (err, data) => {
      if (err) {
        hilog.error(0x0000, 'EntryAbility', 'Failed to load the content.');
      }
    });
  }
}

四、关键技术与原理分析

4.1 方向控制原理

  • setPreferredOrientation:核心方法,控制窗口显示方向
  • FOLLOW_SENSOR:跟随传感器自动旋转,受系统旋转锁控制
  • 媒体查询:监听屏幕方向变化事件

4.2 布局适配技巧

使用条件渲染和Flex布局实现响应式设计:

typescript 复制代码
// 响应式布局示例
build() {
  Column() {
    if (this.currentOrientation === 'landscape') {
      // 横屏布局
      Row() {
        // 左右分栏
      }
    } else {
      // 竖屏布局  
      Column() {
        // 上下分栏
      }
    }
  }
}

五、完整项目结构

css 复制代码
entry/src/main/ets/
├── entryability/EntryAbility.ets
├── pages/VideoPlayback.ets
├── utils/OrientationUtil.ets
└── resources/  // 资源文件

六、测试与调试要点

  1. 真机测试:在鸿蒙设备上测试旋转效果
  2. 旋转锁定:测试系统旋转开关的影响
  3. 折叠屏适配:考虑折叠态和展开态的不同场景

七、常见问题解决

问题1 :旋转后布局错乱 解决:使用媒体查询监听方向变化,动态调整布局

问题2 :旋转动画卡顿
解决:优化布局计算,避免复杂操作在旋转时执行

这个完整示例涵盖了鸿蒙6中ArkTS横竖屏适配的核心技术点,适合初学者逐步学习和实践。

相关推荐
威哥爱编程42 分钟前
HarmonyOS 6.0 蓝牙实现服务端和客户端通讯案例详解
harmonyos
威哥爱编程1 小时前
鸿蒙6开发中,通过文本和字节数组生成码图案例
harmonyos·arkts·arkui
kirk_wang1 小时前
HarmonyOS碰一碰赋能电商场景:订单信息无缝分享的落地实践
华为·harmonyos
n***i951 小时前
HarmonyOS在智能穿戴中的可穿戴设备
华为·harmonyos
SuperHeroWu72 小时前
【HarmonyOS 6】UIAbility跨设备连接详解(分布式软总线运用)
分布式·华为·harmonyos·鸿蒙·连接·分布式协同·跨设备链接
lqj_本人3 小时前
鸿蒙Cordova开发踩坑记录:音频焦点的“独占欲“
华为·harmonyos
柒儿吖3 小时前
Electron for 鸿蒙PC - Native模块Mock与降级策略
javascript·electron·harmonyos
用户463989754325 小时前
Harmony os——AbilityStage 组件管理器:我理解的 Module 级「总控台」
harmonyos
用户463989754325 小时前
Harmony os——UIAbility 组件基本用法:启动页、Context、终止与拉起方信息全流程
harmonyos