使用ArkTS语言实现视频播放器,主要包括主界面和视频播放界面功能:

主界面顶部使用Swiper组件实现视频海报轮播。 主界面下方使用List组件实现视频列表。 播放界面使用Video组件实现视频播放。 在不使用视频组件默认控制器的前提下,实现自定义控制器。 播放界面底部使用图标控制视频播放/暂停。 播放界面底部使用Slider组件控制和实现视频播放进度。 播放界面使用Stack容器组件的Z序控制在视频播放画面上展示开始/暂停/加载图标。

注意:主界面中最近播放和为你推荐列表播放网络视频,需将CommonConstants.ets中的NET属性修改为网络视频地址,网络视频要在真机上运行。

完整示例

HarmonyOS-UI: HarmonyOS一些UI示例

图片展示

软件要求

DevEco Studio版本:DevEco Studio 3.1 Release。HarmonyOS SDK版本:API version 9。

硬件要求

设备类型:华为手机或运行在DevEco Studio上的华为手机设备模拟器。HarmonyOS系统:3.1.0 Developer Release。

代码结构

├──entry/src/main/ets // 代码区│ ├──common│ │ └──constants│ │     └──CommonConstants.ets // 样式常量类│ ├──entryability│ │ └──EntryAbility.ts // 程序入口类│ ├──pages│ │ ├──SimpleVideoIndex.ets // 主界面│ │ └──SimpleVideoPlay.ets // 视频播放界面│ ├──view│ │ ├──VideoModule.ets // 自定义首页List模块组件文件│ │ ├──SwiperVideo.ets // 自定义首页Swiper组件文件│ │ ├──VideoPlayer.ets // 自定义播放界面视频组件文件│ │ └──VideoPlaySlider.ets // 自定义播放界面视频进度条组件文件│ └──viewmodel│ ├──HorizontalVideoItem.ets // 水平视频类│ ├──ParamItem.ets // 参数类│ ├──SwiperVideoItem.ets // banner视频类│ └──SwiperVideoModelets // 首页相关数据└──entry/src/main/resource // 应用静态资源目录

构建主界面

主界面由视频轮播模块和多个视频列表模块组成

主界面代码 SimpleVideoIndex

import { COMMON_PERCENT, COMMON_SIZE, LIST } from '../common/constants/CommonConstants'

import { SwiperVideo } from '../view/SwiperVideo'

import { VideoModule } from '../view/VideoModule'

@Entry

@Component

struct SimpleVideoIndex {

build() {

Column({ space: COMMON_SIZE.SIZE_24 }) {

SwiperVideo()

List() {

ForEach(LIST, (item: string) => {

ListItem() {

VideoModule({ moduleName: item })

.margin({ top: COMMON_SIZE.SIZE_12 })

}

}, (item: string) => JSON.stringify(item))

}

.margin({ top: COMMON_SIZE.SIZE_18 })

}

.width(COMMON_PERCENT.PERCENT_100)

.height(COMMON_PERCENT.PERCENT_100)

.backgroundColor($r("app.color.index_backgroundColor"))

}

}

视频播放核心代码及流程

以视频轮播为例

1.获取视频数据源

在SwiperVideoModel类中定义,通过SwiperVideoModel.getSwiperVideoItems()拿到数据源

2.跳转播放页面

点击视频通过Navigator的target携带跳转路径

Navigator({ target: 'pages/SimpleVideoPlay', type: NavigationType.Push }) {

Image(this.imageSrc)

.borderRadius(COMMON_SIZE.SIZE_12)

}

.params(this.paramItem)

3.构建视频播放界面

3.1  通过Stack()容器以及zIndex属性进行组件堆叠,包括图片、播放图标、视频加载图标、播放控制

3.2 视频播放子组件VideoPlayer ,onPrepared回调方法中可以获取视频总时长,onUpdate回调方法中可实时获取到视频播放的当前时间戳,onFinish是视频播放结束后的回调方法,onError是视频播放出错的回调方法。

3.3  在自定义组件VideoPlayer底部使用了自定义子组件VideoSlider,VideoSlider自定义组件中显示和控制视频播放进度、播放、暂停、快进功能。

SimpleVideoPlay代码

import router from '@ohos.router';

import {

COMMON_NUM_FONT_WEIGHT,

COMMON_PERCENT,

COMMON_SIZE,

STACK_STYLE

} from '../common/constants/CommonConstants';

import { VideoPlayer } from '../view/VideoPlayer';

@Entry

@Component

struct SimpleVideoPlay {

private source: string = (router.getParams() as Record).source as string;

private startIconResource: Resource = $r('app.media.ic_public_play');

private backIconResource: Resource = $r('app.media.ic_back');

@Provide isPlay: boolean = false;

@Provide isOpacity: boolean = false;

private controller: VideoController = new VideoController();

@Provide isLoading: boolean = false;

@Provide progressVal: number = 0;

@Provide flag: boolean = false;

aboutToAppear() {

this.source;

}

onPageHide() {

this.controller.pause();

}

build() {

Column() {

Row() {

Image(this.backIconResource)

.width(COMMON_SIZE.SIZE_24)

.height(COMMON_SIZE.SIZE_24)

.margin({ left: COMMON_SIZE.SIZE_24 })

.onClick(() => {

router.back();

})

Text($r('app.string.back'))

.fontColor(Color.White)

.fontSize(COMMON_SIZE.SIZE_24)

.fontWeight(COMMON_NUM_FONT_WEIGHT)

.margin({ left: COMMON_SIZE.SIZE_12 })

}

.width(COMMON_PERCENT.PERCENT_100)

.margin({

left: COMMON_SIZE.SIZE_12,

top: COMMON_SIZE.SIZE_12

})

.justifyContent(FlexAlign.Start)

Stack() {

if (!this.isPlay && !this.isLoading) {

Image(this.startIconResource)

.width(COMMON_SIZE.SIZE_50)

.height(COMMON_SIZE.SIZE_50)

.zIndex(STACK_STYLE.IMAGE_Z_INDEX)

.onClick(() => {

if (this.flag === true) {

this.isPlay = true

this.controller.start()

}

})

}

if (this.isLoading) {

Progress({

value: STACK_STYLE.PROGRESS_VALUE,

total: STACK_STYLE.PROGRESS_TOTAL,

type: ProgressType.ScaleRing

})

.color(Color.Grey)

.value(this.progressVal)

.width(STACK_STYLE.PROGRESS_WIDTH)

.style({

strokeWidth: STACK_STYLE.PROGRESS_STROKE_WIDTH,

scaleCount: STACK_STYLE.PROGRESS_SCALE_COUNT,

scaleWidth: STACK_STYLE.PROGRESS_SCALE_WIDTH

})

.zIndex(STACK_STYLE.PROGRESS_Z_INDEX)

}

VideoPlayer({

source: this.source,

controller: this.controller

})

.zIndex(0)

}

}

.height(COMMON_PERCENT.PERCENT_100)

.backgroundColor(Color.Black)

}

}

VideoPlayer代码

import prompt from '@ohos.promptAction';

import { COMMON_NUM, COMMON_PERCENT, MESSAGE, SPLIT, START_TIME, ZERO_STR } from '../common/constants/CommonConstants';

import { VideoPlaySlider } from './VideoPlaySlider';

/**

* video controller component

*/

@Component

export struct VideoPlayer {

private source: string | Resource = '';

private controller: VideoController = new VideoController();

private previewUris: Resource = $r('app.media.preview');

@Provide currentTime: number = 0;

@Provide durationTime: number = 0;

@Provide durationStringTime: string = START_TIME;

@Provide currentStringTime: string = START_TIME;

@Consume isPlay: boolean;

@Consume isOpacity: boolean;

@Consume flag: boolean;

@Consume isLoading: boolean;

@Consume progressVal: number;

build() {

Column() {

Video({

src: this.source,

previewUri: this.previewUris,

controller: this.controller

})

.width(COMMON_PERCENT.PERCENT_100)

.height(COMMON_PERCENT.PERCENT_88)

.controls(false)

.autoPlay(false)

.objectFit(ImageFit.Contain)

.loop(false)

.onUpdate((event) => {

if (event) {

this.currentTime = event.time;

this.currentStringTime = this.changeSliderTime(this.currentTime);

}

})

.onPrepared((event) => {

this.prepared(event?.duration);

})

.onFinish(() => {

this.finish();

})

.onError(() => {

prompt.showToast({

duration: COMMON_NUM.NUM_5000,

message: MESSAGE

});

})

VideoPlaySlider({ controller: this.controller })

}

}

/**

* video component prepared callback

*/

prepared(duration: number) {

this.durationTime = duration;

let second: number = duration % COMMON_NUM.NUM_60;

let min: number = Number.parseInt((duration / COMMON_NUM.NUM_60).toString());

let head = min < COMMON_NUM.NUM_10 ? `${ZERO_STR}${min}` : min;

let end = second < COMMON_NUM.NUM_10 ? `${ZERO_STR}${second}` : second;

this.durationStringTime = `${head}${SPLIT}${end}`;

this.flag = true;

}

/**

* Get video string of current time.

* @param the number of current time

* @return the string of current time

*/

changeSliderTime(value: number): string {

let second: number = value % COMMON_NUM.NUM_60;

let min: number = Number.parseInt((value / COMMON_NUM.NUM_60).toString());

let head = min < COMMON_NUM.NUM_10 ? `${ZERO_STR}${min}` : min;

let end = second < COMMON_NUM.NUM_10 ? `${ZERO_STR}${second}` : second;

let nowTime = `${head}${SPLIT}${end}`;

return nowTime;

}

/**

* video component finish callback

*/

finish() {

this.isPlay = false;

this.isOpacity = false;

}

}

总结

Swiper组件的使用。List组件的使用。Video组件的使用。Slider组件的使用。如何实现自定义视频控制器。

精彩链接

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。