大家好,我是 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 :旋转动画卡顿
解决:优化布局计算,避免复杂操作在旋转时执行
这个完整示例涵盖了鸿蒙6中ArkTS横竖屏适配的核心技术点,适合初学者逐步学习和实践。
