天下有敌 發表於 2025-11-29 09:25:00

【鸿蒙开发实战篇】鸿蒙6开发视频播放器的屏幕方向适配问题

<p>大家好,我是 V 哥,<br>
在鸿蒙6开发中,屏幕方向适配是提升用户体验的重要环节。下面我将通过一个完整的视频播放器示例,详细讲解ArkTS中横竖屏切换的实现方案。</p>
<p>联系V哥获取 鸿蒙学习资料</p>
<h2 id="一基础概念理解">一、基础概念理解</h2>
<h3 id="11-屏幕方向类型">1.1 屏幕方向类型</h3>
<p>鸿蒙系统支持四种屏幕方向:</p>
<ul>
<li><strong>PORTRAIT</strong>(竖屏):屏幕高度大于宽度</li>
<li><strong>LANDSCAPE</strong>(横屏):屏幕宽度大于高度</li>
<li><strong>PORTRAIT_INVERTED</strong>(反向竖屏)</li>
<li><strong>LANDSCAPE_INVERTED</strong>(反向横屏)</li>
</ul>
<h3 id="12-适配策略">1.2 适配策略</h3>
<ul>
<li><strong>静态配置</strong>:通过配置文件锁定基础方向</li>
<li><strong>动态调整</strong>:运行时感知设备旋转并智能适配</li>
</ul>
<h2 id="二静态配置实现">二、静态配置实现</h2>
<h3 id="21-修改modulejson5配置">2.1 修改module.json5配置</h3>
<p>在<code>src/main/module.json5</code>文件中配置UIAbility的方向属性:</p>
<pre><code class="language-json">{
"module": {
    "abilities": [
      {
      "name": "EntryAbility",
      "orientation": "landscape", // 可选:portrait|landscape|unspecified
      "metadata": [
          {
            "name": "ohos.ability.orientation",
            "value": "$profile:orientation"
          }
      ]
      }
    ]
}
}
</code></pre>
<p><strong>参数说明</strong>:</p>
<ul>
<li><code>portrait</code>:锁定竖屏</li>
<li><code>landscape</code>:锁定横屏</li>
<li><code>unspecified</code>:跟随系统(默认)</li>
</ul>
<h2 id="三动态横竖屏切换实现">三、动态横竖屏切换实现</h2>
<h3 id="31-创建方向工具类">3.1 创建方向工具类</h3>
<p>新建<code>utils/OrientationUtil.ets</code>文件:</p>
<pre><code class="language-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 &gt; 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;
}
</code></pre>
<h3 id="32-视频播放页面实现">3.2 视频播放页面实现</h3>
<p>创建<code>pages/VideoPlayback.ets</code>主页面:</p>
<pre><code class="language-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', () =&gt; {
      this.currentOrientation = OrientationUtil.getCurrentOrientation();
      console.info('屏幕方向变化:', this.currentOrientation);
    });

    // 媒体查询监听横屏事件
    const mediaQuery = mediaquery.matchMediaSync('(orientation: landscape)');
    this.mediaQueryListener = mediaQuery;
    mediaQuery.on('change', (result: mediaquery.MediaQueryResult) =&gt; {
      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(() =&gt; this.toggleFullScreen())

          Button('锁定横屏')
            .width('90%')
            .height(40)
            .margin({ top: 10 })
            .backgroundColor('#FF6A00')
            .fontColor(Color.White)
            .onClick(() =&gt; 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(() =&gt; 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');
}
}
</code></pre>
<h3 id="33-entryability配置">3.3 EntryAbility配置</h3>
<p>更新<code>entryability/EntryAbility.ets</code>:</p>
<pre><code class="language-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) =&gt; {
      if (err) {
      hilog.error(0x0000, 'EntryAbility', 'Failed to get main window');
      return;
      }
      // 初始设置为竖屏,但允许跟随传感器旋转
      windowClass.setPreferredOrientation(window.Orientation.FOLLOW_SENSOR);
    });

    windowStage.loadContent('pages/VideoPlayback', (err, data) =&gt; {
      if (err) {
      hilog.error(0x0000, 'EntryAbility', 'Failed to load the content.');
      }
    });
}
}
</code></pre>
<h2 id="四关键技术与原理分析">四、关键技术与原理分析</h2>
<h3 id="41-方向控制原理">4.1 方向控制原理</h3>
<ul>
<li><strong>setPreferredOrientation</strong>:核心方法,控制窗口显示方向</li>
<li><strong>FOLLOW_SENSOR</strong>:跟随传感器自动旋转,受系统旋转锁控制</li>
<li><strong>媒体查询</strong>:监听屏幕方向变化事件</li>
</ul>
<h3 id="42-布局适配技巧">4.2 布局适配技巧</h3>
<p>使用条件渲染和Flex布局实现响应式设计:</p>
<pre><code class="language-typescript">// 响应式布局示例
build() {
Column() {
    if (this.currentOrientation === 'landscape') {
      // 横屏布局
      Row() {
      // 左右分栏
      }
    } else {
      // 竖屏布局
      Column() {
      // 上下分栏
      }
    }
}
}
</code></pre>
<h2 id="五完整项目结构">五、完整项目结构</h2>
<pre><code>entry/src/main/ets/
├── entryability/EntryAbility.ets
├── pages/VideoPlayback.ets
├── utils/OrientationUtil.ets
└── resources/// 资源文件
</code></pre>
<h2 id="六测试与调试要点">六、测试与调试要点</h2>
<ol>
<li><strong>真机测试</strong>:在鸿蒙设备上测试旋转效果</li>
<li><strong>旋转锁定</strong>:测试系统旋转开关的影响</li>
<li><strong>折叠屏适配</strong>:考虑折叠态和展开态的不同场景</li>
</ol>
<h2 id="七常见问题解决">七、常见问题解决</h2>
<p><strong>问题1</strong>:旋转后布局错乱<br>
<strong>解决</strong>:使用媒体查询监听方向变化,动态调整布局</p>
<p><strong>问题2</strong>:旋转动画卡顿<br>
<strong>解决</strong>:优化布局计算,避免复杂操作在旋转时执行</p>
<p>这个完整示例涵盖了鸿蒙6中ArkTS横竖屏适配的核心技术点,适合初学者逐步学习和实践。</p>


</div>
<div id="MySignature" role="contentinfo">
    <p>本文来自博客园,作者:威哥爱编程,转载请注明原文链接:https://www.cnblogs.com/finally-vince/p/19282903</p><br><br>
来源:https://www.cnblogs.com/finally-vince/p/19282903
頁: [1]
查看完整版本: 【鸿蒙开发实战篇】鸿蒙6开发视频播放器的屏幕方向适配问题