注册/登录

OpenHarmony仿视频播放器应用-爱电影(四)

系统 OpenHarmony
无论是从视频播放页面进入全屏播放页面,还是由全屏播放页面返回到视频播放页面,只要处于播放在,就会同步播放时间,在页面切换后继续播放视频。当然,在全屏播放时页面处于横屏,返回到视频播放页面界面则切换回竖屏。

想了解更多关于开源的内容,请访问:

​51CTO 开源基础软件社区​

​https://ost.51cto.com​

效果

​在线视频​

接​ ​上一篇​ ​,视频播放页面属于小屏显示,为了让观演效果更好,可以选择全屏播放,全屏播放时界面由竖屏转为横屏显示,并且可以双向同步观影时间,无论是从视频播放页面进入全屏播放页面,还是由全屏播放页面返回到视频播放页面,只要处于播放在,就会同步播放时间,在页面切换后继续播放视频。当然,在全屏播放时页面处于横屏,返回到视频播放页面界面则切换回竖屏,我们来看下设计图:

#创作者激励#OpenHarmony仿视频播放器应用-爱电影(四)-开源基础软件社区

#创作者激励#OpenHarmony仿视频播放器应用-爱电影(四)-开源基础软件社区

从设计图上看,全屏播放页面的布局很简单,我们在上一节总已经将视频播放视图封装成了一个子组件—VideoView.ets,我们只要将其加载到全屏播放页面即可。

项目开发

开发环境

硬件平台:DAYU2000 RK3568
系统版本:OpenHarmony 3.2 beta5
SDK:9(3.2.10.6)
IDE:DevEco Studio 3.1 Beta1 Build Version: 3.1.0.200, built on February 13, 2023

程序代码

1、FullScreen.ets

/**
* 全屏播放
*/
import emitter from '@ohos.events.emitter';
import { CommonData } from '../model/CommonData'
import router from '@ohos.router';
import { VideoView } from '../view/VideoView';
import { VideoData } from '../model/VideoData'
import { VideoDataUtils } from '../utils/VideoDataUtils'
import { VideoSpeed } from '../model/VideoSpeed'
import { PLAYBACK_SPEED, PLAYBACK_STATE } from '../model/Playback'
const TAG: string = 'VideoFullScreen'
@Entry
@Component
struct VideoFullScreen {
@State mTag: string = TAG
@State mVideoData: VideoData = null
private name: string
@State uri: any = null
@State previewImage: any = null
private actors: string | Resource
private directs: string | Resource
private introduction: string
@State videoState: string = PLAYBACK_STATE.INIT
@Provide('play_time') curTime: number = 0
@State rateIndex: number = 1
@State rate: VideoSpeed = PLAYBACK_SPEED[1]
@Provide('show_operation') isShowOperation : boolean = true
aboutToAppear() {
// 横屏显示
emitter.emit({
eventId: CommonData.EVENT_WINDOW_LANDSCAPE_ID
})
this.initData()
}
initData() {
// 获取当前需要播放的电影资源信息
this.mVideoData = router.getParams()['video_data']
this.name = this.mVideoData.name
this.uri = this.mVideoData.uri
this.previewImage = this.mVideoData.image
this.actors = VideoDataUtils.getUser(this.mVideoData.actors)
this.directs = VideoDataUtils.getUser(this.mVideoData.directs)
this.introduction = this.mVideoData.introduction
this.curTime = router.getParams()['cur_time']
this.videoState = router.getParams()['video_state']
console.info(`${TAG} curTime:${this.curTime} videoState:${this.videoState}`)
}
onBackPress() {
console.info(`${TAG} onBackPress`)
this.sendPlayVideo()
}
onScreen(isFull: boolean) {
console.info(`${TAG} onScreen ${isFull}`)
if (!isFull) {
this.goBack()
}
}
sendPlayVideo() {
console.info(`${TAG} sendPlayVideo`)
emitter.emit({
eventId: CommonData.EVENT_PLAY_VIDEO
}, {
data: {
cur_time: this.curTime,
video_state: this.videoState
}
})
}
goBack() {
this.sendPlayVideo()
router.back()
}
aboutToDisappear() {
}
build() {
Stack({
alignContent: Alignment.TopStart
}) {
VideoView({
_TAG: this.mTag,
videoUri: $uri,
previewUri: $previewImage,
videoRate: $rate,
videoRateIndex: $rateIndex,
onScreen: this.onScreen.bind(this),
videoState: $videoState,
isFullScreen: true,
isEvent: false,
mWidth: '100%',
mHeight: '100%'
})
if (this.isShowOperation) {
Row({ space: 10 }) {
Image($r('app.media.icon_back'))
.width(24)
.height(24)
.objectFit(ImageFit.Cover)
.onClick(() => {
this.goBack()
})
Text(this.name)
.fontSize(20)
.fontColor(Color.White)
}
.padding(20)
}
}
.width('100%')
.height('100%')
}
}

界面代码非常简单,所有的功能在集成在VideoView组件中,这与视频播放页面相比,增加了电影播放倍数的选择,选择器使用​ ​Select​ ​下拉选择菜单实现,下面我们来详细的介绍下这个组件。

Select

提供了下拉选择菜单,让用户在多个选项之间选择。

Select(options: Array<SelectOption>)
SelectOption对象说明:

参数名

参数类型

必填

参数描述

value

ResourceStr

下拉选项内容。

icon

ResourceStr

下拉选项图片。

属性:

名称

参数类型

描述

selected

number

设置下拉菜单初始选项的索引,第一项的索引为0。

当不设置selected属性时,默认选择值为-1,菜单项不选中。

value

string

设置下拉按钮本身的文本内容。

font

Font

设置下拉按钮本身的文本样式。

fontColor

ResourceColor

设置下拉按钮本身的文本颜色。

selectedOptionBgColor

ResourceColor

设置下拉菜单选中项的背景色。

selectedOptionFont

Font

设置下拉菜单选中项的文本样式。

selectedOptionFontColor

ResourceColor

设置下拉菜单选中项的文本颜色。

optionBgColor

ResourceColor

设置下拉菜单项的背景色。

optionFont

Font

设置下拉菜单项的文本样式。

optionFontColor

ResourceColor

设置下拉菜单项的文本颜色。

事件:

名称

功能描述

onSelect(callback: (index: number, value?: string) => void)

下拉菜单选中某一项的回调。<br/>index:选中项的索引。<br/>value:选中项的值。

本案例中的Select组件是在VideoView.ets视频播放子组件中实现的,核心代码如下:

VideoView.ets

if (this.isFullScreen) {
Select(this.selectSpeedOption)
.selected(this.videoRateIndex)
.value(this.videoRate.val)
.font({ size: 10 })
.fontColor(Color.White)
.selectedOptionFont({ size: 10 })
.selectedOptionFontColor('#F54F02')
.optionFontColor('#5E5E5E')
.optionFont({ size: 10 })
.onSelect((index: number) => {
console.info('Select:' + index)
this.videoRate = PLAYBACK_SPEED[index]
this.videoRateIndex = index
console.info(`${TAG} videoRateIndex = ${this.videoRateIndex}`)
})
.border({
width: 0,
color: Color.White
})
}

2、横竖屏切换

如何实现横竖屏切换:

首先我们知道由于的界面需要集成到一个窗口上,这个窗口就是Window,在应用启动时会触发UIAbility的生命周期方法onWindowStageCreate(),此接口的回调中带有一个参数就是WindowStage窗口管理器,窗口管理器可以通过getMainWindow()接口获取到主窗口,返回当前窗口的实例Window,得到窗口实例后就可以通过setPreferredOrientation()设置窗口的显示方向。

setPreferredOrientation:
setPreferredOrientation(orientation: Orientation, callback: AsyncCallback<void" style="font: revert; -webkit-font-smoothing: antialiased; margin: 0px; padding: 0px; border: 0px; vertical-align: baseline; color: rgb(166, 127, 89); cursor: help;">>): void

设置窗口的显示方向属性,使用callback异步回调。

参数:

参数名

类型

必填

说明

Orientation

Orientation

窗口显示方向的属性。

callback

AsyncCallback<void>

回调函数。

Orientation:

窗口显示方向类型枚举。

名称

说明

UNSPECIFIED

0

表示未定义方向模式,由系统判定。

PORTRAIT

1

表示竖屏显示模式。

LANDSCAPE

2

表示横屏显示模式。

PORTRAIT_INVERTED

3

表示反向竖屏显示模式。

LANDSCAPE_INVERTED

4

表示反向横屏显示模式。

AUTO_ROTATION

5

表示传感器自动旋转模式。

AUTO_ROTATION_PORTRAIT

6

表示传感器自动竖向旋转模式。

AUTO_ROTATION_LANDSCAPE

7

表示传感器自动横向旋转模式。

AUTO_ROTATION_RESTRICTED

8

表示受开关控制的自动旋转模式。

AUTO_ROTATION_PORTRAIT_RESTRICTED

9

表示受开关控制的自动竖向旋转模式。

AUTO_ROTATION_LANDSCAPE_RESTRICTED

10

表述受开关控制的自动横向旋转模式。

LOCKED

11

表示锁定模式。

具体如何实现呢?
我们知道由于启动时会加重UIAbility,在项目中EntryAbility继承UIAbility,所以可以在EntryAbility.ts中获取Window实例设置其窗口显示方向来实现横竖屏切换,代码如下:

import UIAbility from '@ohos.app.ability.UIAbility';
import hilog from '@ohos.hilog';
import window from '@ohos.window';
import emitter from '@ohos.events.emitter';
import { CommonData } from '../model/CommonData'
export default class EntryAbility extends UIAbility {
private mWindow : window.Window
onCreate(want, launchParam) {

}
onDestroy() {

// 设置竖屏
this.mWindow.setPreferredOrientation(window.Orientation.PORTRAIT)
this.unregisterEmitter()
}
onWindowStageCreate(windowStage: window.WindowStage) {
// Main window is created, set main page for this ability
this.mWindow = windowStage.getMainWindowSync()
this.registerEmitter()
windowStage.loadContent('pages/Splash', (err, data) => {
if (err.code) {
return;
}
});
}

registerEmitter() {
emitter.on({
eventId : CommonData.EVENT_WINDOW_PORTRAIT_ID
}, () => {
if (!this.mWindow) {
return
}
this.mWindow.setPreferredOrientation(window.Orientation.PORTRAIT)
})
emitter.on({
eventId : CommonData.EVENT_WINDOW_LANDSCAPE_ID
}, () => {
if (!this.mWindow) {
return
}
this.mWindow.setPreferredOrientation(window.Orientation.LANDSCAPE)
})
}
unregisterEmitter() {
emitter.off(CommonData.EVENT_WINDOW_PORTRAIT_ID)
emitter.off(CommonData.EVENT_WINDOW_LANDSCAPE_ID)
}
}

由于视频播放页面和全屏播放页面与EntryAbility无直接联系,如果在操作页面时修改窗口方向呢?我相信你也注意到了上面的代码中使用到了@ohos.events.emitter,​emitter提供了在同一进程不同线程之间或者同一进程同一线程内,发送和处理事件的能力,可以通过订阅事件、取消订阅、发送事件等接口实现消息线程通信。所以我们在EntryAbility的onWindowStageCreate()接口回调时订阅了横竖屏切换事件,当然在应用退出时,也就是在onDestroy()接口被回调时,应该注取消订阅,防止内存泄漏,消息错乱。

发送横竖屏切换事件:
  • 播放页面切换到全屏播放时界面切换成横屏,需要在FullScreen.ets界面被启动回调aboutToAppear()接口时发送横屏事件,通知Window修改方向。FullScreen.ets中的核对代码:
aboutToAppear() {
// 横屏显示
emitter.emit({
eventId: CommonData.EVENT_WINDOW_LANDSCAPE_ID
})
}
  • 全屏播放返回到视频播放页时需要将横屏切换到竖屏显示,所以当Playback.ets页面的onPageShow()接口被触发时,就发送竖屏事件,通知Window修改方向。Playback.ets中的核心代码:
onPageShow() {
// 竖屏显示
emitter.emit({
eventId: CommonData.EVENT_WINDOW_PORTRAIT_ID
})
}

这样就完成了视频播放页面为竖屏,全屏播放为横屏的功能。

3、播放时间同步

播放时间同步主要在视频播放页面与全屏播放页面相互切换时使用,在两个页面切换时,除了时间同步外,播放状态也需要同步。时间同步是指:视频播放页面在播放视频时,假设播放到5s这个时间帧节点时,切换到全屏播放页面,全屏播放进入播放状态,且从5s这个时间帧节点开始播放。

如上所述,两个页面之间必须同步播放时间戳,页面切换通过路由器@ohos. router 实现,在router.pushUrl()函数中可以添加参数,我们将时间戳通过自定义参数传递到目标界面,页面返回到上一级页面时,一般使用router.back(),此时通过发送事件同步消息实现视频播放时间同步。具体实现请参看FullScreen.ets、Playback.ets、VideoView.ets三个类。

这个就是全屏播放页面的实现,到目前已经将视频播放器的所有页面实现讲述完毕。

想了解更多关于开源的内容,请访问:

​51CTO 开源基础软件社区​

​https://ost.51cto.com​

责任编辑:jianghua 51CTO 开源基础软件社区
点赞
收藏