HarmonyOS7 视频播放:Video 组件从入门到自定义控制器
前言
视频播放这功能,看着简单——不就是个 Video 组件嘛。但真要做得像样,自定义控制器、全屏切换、手势控进度,一个比一个折腾。HarmonyOS7 自带的 Video 组件默认控制器功能有限,样式也不可控。想要自己的播放器 UI,就得手动搞一套。这篇文章从最基础的用法讲起,一路到自定义控制器 + 手势控制,够你做出一个像样的视频播放器。
我的目标是:看完这篇,你能做出一个带自定义进度条、播放/暂停按钮、全屏切换、手势拖动进度的视频播放器。HarmonyOS7 的 Video 组件本身能力是够的,关键在于怎么把默认控制器关掉,自己接管所有交互逻辑。
Video 组件基础
最简单的用法,三行代码就能播放视频:
@State videoSrc: Resource = $rawfile('demo.mp4');
controller: VideoController = new VideoController();
Video({ src: this.videoSrc, controller: this.controller })
.width('100%')
.height(300)
支持的视频源类型:
| 类型 | 示例 |
|---|---|
| 本地 rawfile | $rawfile('video.mp4') |
| 网络地址 | 'https://example.com/video.mp4' |
| 沙箱 URI | 'file:///data/storage/.../video.mp4' |
用网络地址需要在
module.json5里申请ohos.permission.INTERNET权限。
Video 组件的常用属性:
Video({ src: this.videoSrc, controller: this.controller })
.autoPlay(false)
.loop(false)
.controls(true)
.objectFit(ImageFit.Contain)
.onStart(() => { console.info('开始播放'); })
.onPause(() => { console.info('暂停'); })
.onFinish(() => { console.info('播放结束'); })
.onPrepared((e) => {
if (e) console.info(`视频时长: ${e.duration}秒`);
})
.onUpdate((e) => {
if (e) console.info(`当前进度: ${e.time}秒`);
})
.onFullscreenChange((e) => {
if (e) console.info(`全屏状态: ${e.fullscreen}`);
})
属性讲解:
autoPlay— 是否自动播放,默认 falseloop— 是否循环播放controls— 是否显示默认控制器,自定义控制器时要设为 falseobjectFit— 视频填充模式,Contain保持比例不裁剪onPrepared— 视频加载完成,e.duration是总时长onUpdate— 播放进度回调,e.time是当前秒数
自定义播放控制
默认控制器关掉后(controls(false)),所有交互都得自己实现。先看完整代码:
@Component
export struct CustomVideoPlayer {
@State videoSrc: Resource = $rawfile('demo.mp4');
@State isPlaying: boolean = false;
@State currentTime: number = 0;
@State duration: number = 0;
@State showControls: boolean = true;
controller: VideoController = new VideoController();
build() {
Stack({ alignContent: Alignment.Bottom }) {
Video({ src: this.videoSrc, controller: this.controller })
.width('100%')
.height(300)
.controls(false)
.autoPlay(false)
.onPrepared((e) => {
if (e) this.duration = e.duration;
})
.onUpdate((e) => {
if (e) this.currentTime = e.time;
})
.onFinish(() => {
this.isPlaying = false;
})
.onClick(() => {
this.showControls = !this.showControls;
})
if (this.showControls) {
Column() {
Slider({
value: this.currentTime,
min: 0,
max: this.duration,
step: 1
})
.width('90%')
.onChange((value) => {
this.controller.setCurrentTime(value);
})
Row({ space: 20 }) {
Button(this.isPlaying ? '暂停' : '播放')
.onClick(() => {
if (this.isPlaying) {
this.controller.pause();
} else {
this.controller.start();
}
this.isPlaying = !this.isPlaying;
})
Text(`${this.formatTime(this.currentTime)} / ${this.formatTime(this.duration)}`)
.fontColor(Color.White)
.fontSize(12)
}
.width('90%')
.justifyContent(FlexAlign.Center)
}
.width('100%')
.padding(10)
.backgroundColor('#88000000')
}
}
}
formatTime(seconds: number): string {
let min = Math.floor(seconds / 60);
let sec = Math.floor(seconds % 60);
return `${min.toString().padStart(2, '0')}:${sec.toString().padStart(2, '0')}`;
}
}
逐行讲解自定义控制器核心逻辑:
controls(false)— 关掉默认控制器,这是自定义的前提onClick切换showControls— 点击视频区域显示/隐藏控制栏Slider组件绑定currentTime和duration— 进度条跟随播放进度实时更新onChange里调用controller.setCurrentTime(value)— 用户拖动进度条时跳转播放位置- 播放/暂停按钮通过
controller.start()/controller.pause()控制 formatTime把秒数格式化成mm:ss格式
关键点:
isPlaying状态要自己维护。onFinish时别忘了置为 false,不然按钮显示会错。
全屏切换
全屏切换用 controller.requestFullscreen() 方法:
@State isFullscreen: boolean = false;
Button(this.isFullscreen ? '退出全屏' : '全屏')
.onClick(() => {
if (this.isFullscreen) {
this.controller.exitFullscreen();
} else {
this.controller.requestFullscreen(true);
}
})
// 监听全屏状态变化
.onFullscreenChange((e) => {
if (e) this.isFullscreen = e.fullscreen;
})
讲解:
requestFullscreen(true)— 参数 true 表示横屏全屏,false 为竖屏exitFullscreen()— 退出全屏- 必须监听
onFullscreenChange来同步状态,因为用户也可能通过系统手势退出全屏
全屏时视频容器的尺寸也要跟着变:
Video({ src: this.videoSrc, controller: this.controller })
.width(this.isFullscreen ? '100%' : '100%')
.height(this.isFullscreen ? '100%' : 300)
小坑:全屏状态下,自定义控制栏的布局可能需要调整。建议全屏时把控制栏放在底部,加个半透明背景。
手势控制进度
拖动手势调进度,这是短视频 App 的标配功能。用 PanGesture 实现:
@State panStartTime: number = 0;
@State panDeltaX: number = 0;
Video({ src: this.videoSrc, controller: this.controller })
.controls(false)
.gesture(
PanGesture({ fingers: 1, direction: PanDirection.HORIZONTAL })
.onActionStart(() => {
this.panStartTime = this.currentTime;
this.showControls = false;
})
.onActionUpdate((e) => {
// 水平滑动距离转换为时间偏移
let totalWidth = 300; // 视频容器宽度
let deltaSeconds = (e.offsetX / totalWidth) * this.duration;
let targetTime = Math.max(0, Math.min(this.duration, this.panStartTime + deltaSeconds));
this.currentTime = targetTime;
})
.onActionEnd(() => {
this.controller.setCurrentTime(this.currentTime);
this.showControls = true;
})
)
逐行讲解:
PanDirection.HORIZONTAL— 只响应水平方向的手势onActionStart记录拖动开始时的播放时间onActionUpdate根据手指水平偏移量计算新的播放位置。偏移量 / 容器宽度 × 总时长 = 时间偏移onActionEnd手指抬起后,调用setCurrentTime跳到目标位置Math.max(0, Math.min(this.duration, ...))防止进度超出范围
性能优化
视频播放是吃资源的大户,几个优化建议:
- 及时释放控制器:页面销毁时调用
controller.stop()释放资源 - 预加载封面图:用
previewUri设置视频封面,避免黑屏等待
Video({ src: this.videoSrc, controller: this.controller, previewUri: $rawfile('cover.jpg') })
- 按需加载:只有视频进入可视区域时才初始化播放器
- 避免同时播放多个视频:同时解码多个视频流非常吃内存
- 网络视频用 HLS 协议:支持分段加载,起播更快
写在最后
自定义视频播放器确实比直接用默认控制器费劲不少,但好处是 UI 完全可控,交互想怎么设计都行。我建议先从基础用法入手,跑通了再一步步加自定义控制、全屏、手势。一口吃不成胖子,但一步步来,最后的效果肯定比默认的好看。
更多推荐

所有评论(0)