文章目录
-
- 前言
- [AVPlayer 是什么](#AVPlayer 是什么)
- 画面载体:XComponent
- 播放控制
- 完整播放器骨架
- 常见误区(小白必踩)
- [完整可运行示例:AVPlayer 播音频](#完整可运行示例:AVPlayer 播音频)
- 写在最后
前言
做个能播放音视频的 app,听起来高大上,其实 HarmonyOS 已经把最难的编解码封装好了,你只要学会 AVPlayer 这个播放器对象怎么用就行。

这篇文章我带你实现一个"播放/暂停/进度"俱全的本地视频播放器。网络视频套路一样,只是地址不同。
AVPlayer 是什么
AVPlayer 是系统提供的多媒体播放器,负责"拿到资源 → 解码 → 输出到界面"。你不用懂编解码,只要:设置数据源、准备、播放、控制。

代码实现
typescript
import { media } from '@kit.MediaKit'
let player: media.AVPlayer
async function initPlayer(surfaceId: string, url: string) {
player = await media.createAVPlayer()
player.url = url // 设置播放地址
player.surfaceId = surfaceId // 绑定到 XComponent 的渲染表面
}
要点:
createAVPlayer()异步创建播放器。player.url:本地或网络媒体地址。player.surfaceId:播放画面要"贴"在哪个渲染表面上,这个表面由XComponent提供(见下)。
画面载体:XComponent
视频画面不能直接用普通组件显示,得用 XComponent 提供一块"原生渲染表面":
核心代码
typescript
@Local surfaceId: string = ''
build() {
Column() {
XComponent({
id: 'video',
type: XComponentType.SURFACE,
controller: this.xcCtrl
})
.onLoad((surfaceId: string) => {
this.surfaceId = surfaceId // 拿到表面 id
})
.width('100%')
.height(240)
}
}
XComponent的onLoad回调会给你一个surfaceId,把它存起来传给player.surfaceId,画面就出来了。- 必须先有
surfaceId,才能初始化播放器------所以初始化要等onLoad之后。

播放控制
播放器有几个核心状态,控制就围绕它们:
完整示例
typescript
function play() { player.play() } // 播放
function pause() { player.pause() } // 暂停
function seekTo(ms: number) { player.seek(ms) } // 拖到指定毫秒
// 监听状态与进度
player.on('stateChange', (state) => {
console.info('播放器状态:' + state) // 'playing' / 'paused' / 'completed'
})
player.on('timeUpdate', (cur: number, dur: number) => {
this.progress = cur / dur // 计算播放进度比例
})
play()/pause():最基础的播放暂停。seek(ms):跳转到指定时间,做进度条拖动用。timeUpdate事件:持续回调当前时间和总时长,用来更新进度条。
完整播放器骨架
typescript
@Entry
@ComponentV2
struct VideoPlayer {
@Local surfaceId: string = ''
@Local progress: number = 0
@Local playing: boolean = false
private xcCtrl: XComponentController = new XComponentController()
private player?: media.AVPlayer
build() {
Column() {
XComponent({ id: 'v', type: XComponentType.SURFACE, controller: this.xcCtrl })
.onLoad(async (id) => {
this.surfaceId = id
this.player = await media.createAVPlayer()
this.player.url = 'file:///data/storage/el2/base/haps/entry/files/demo.mp4'
this.player.surfaceId = id
this.player.on('timeUpdate', (c, d) => { this.progress = d ? c / d : 0 })
})
.width('100%').height(240)
Row() {
Button(this.playing ? '暂停' : '播放')
.onClick(() => {
if (this.playing) { this.player?.pause(); this.playing = false }
else { this.player?.play(); this.playing = true }
})
Slider({ value: this.progress * 100, min: 0, max: 100 })
.onChange((v) => { this.player?.seek(v / 100 * 1000) })
}
}
.padding(12)
}
}
这是一个能播、能暂停、能拖进度的播放器雏形。
常见误区(小白必踩)
| 误区 | 说明 |
|---|---|
surfaceId 没拿到就初始化播放器 |
必须先等 XComponent.onLoad 拿到表面 id,再创建 AVPlayer 绑定。 |
不监听 timeUpdate |
进度条要的当前/总时长都从这里来,不监听就做不了进度条。 |
| 音频也套 XComponent | 纯音频不需要画面载体,直接 createAVPlayer 设 url 播放即可。 |
下面这段代码可以直接复制到 DevEco Studio 里运行。建议你边读边敲,改一改文末「动手改一改」里的参数,亲眼看看效果。
完整可运行示例:AVPlayer 播音频
用 AVPlayer 播放一段网络音频,带播放/暂停控制。
代码解析
typescript
// AVPlayer 播放网络音频
import { media } from '@kit.MediaKit'
@Entry
@Component
struct AudioPlayer {
private avp?: media.AVPlayer
@State playing: boolean = false
aboutToAppear() {
media.createAVPlayer().then((p) => {
this.avp = p
p.url = 'https://example.com/song.mp3' // 设置数据源
p.on('stateChange', (s) => {
if (s === 'playing') { this.playing = true }
if (s === 'paused') { this.playing = false }
})
})
}
build() {
Column({ space: 16 }) {
Text(this.playing ? '播放中' : '已暂停').fontSize(20)
Button(this.playing ? '暂停' : '播放')
.onClick(() => {
if (this.playing) { this.avp?.pause() } else { this.avp?.play() }
})
}.padding(20)
}
}
你会看到什么 :进入页面后点「播放」,文字变「播放中」并听到声音;点「暂停」停止。状态由 stateChange 事件驱动 UI。
动手改一改:
- 把
url换成你自己的音频地址验证。 - 监听
timeUpdate事件,做一个进度条。 - 在
aboutToDisappear里this.avp?.release(),避免资源泄漏。
写在最后
AVPlayer 的套路:建播放器 → 绑 surfaceId → 设 url → 监听进度 → 控制播放 。画面靠 XComponent 出,控制靠 play/pause/seek。
音频播放更简单------不需要 XComponent,直接 createAVPlayer 设 url 播放即可。做音乐 app 的话,把视频那套 surface 部分去掉就行。