为一篇 HarmonyOS7 VideoPlaybackStatePanel 教程绘制一张手绘笔记风

前言

很多播放器页面,一开始都只关心按钮能不能点。点一下切播放,再点一下切暂停,似乎就够了。

但只要页面稍微真实一点,你很快就会发现,按钮只是表面。真正需要被管理的是状态。

播放器当前是播放中还是暂停中?用户点了多少次播放?停止以后要不要清空某些信息?这些都不是按钮文案的问题,而是状态流转的问题。

这份案例虽然很轻量,但它已经把这件事的骨架搭出来了。

这份案例最核心的两个状态

先记住这两行:

@State isPlaying: boolean = false
@State playCount: number = 0

一个负责表示“当前是否正在播放”,一个负责记录“播放动作被触发了多少次”。

只靠这两个状态,页面就已经能同步做出很多反馈:

  • 按钮文案变化
  • 颜色变化
  • 状态提示变化
  • 播放次数统计
  • 模拟播放器区域样式变化

完整代码

/**
 * VideoPlaybackStatePanel - Video 播放状态
 * 演示 Video 的播放/暂停状态管理
 */
import { PRESET_COLORS, generateListItems, PAGE_BG_COLOR, PANEL_BG_COLOR, ACCENT_COLOR, MUTED_TEXT_COLOR } from './types'

@Entry
@Component
struct VideoPlaybackStatePanel {
  @State isShow: boolean = true
  @State isPlaying: boolean = false
  @State playCount: number = 0

  togglePlay(): void {
    this.isPlaying = !this.isPlaying
    if (this.isPlaying) {
      this.playCount++
    }
  }

  build() {
    Column() {
      if (this.isShow) {
        Scroll() {
          Column({ space: 16 }) {
            Text('Video 播放状态管理').fontSize(16).fontColor(ACCENT_COLOR).fontWeight(FontWeight.Bold)

            Column() {
              Row() {
                Text(`播放状态: `).fontSize(13).fontColor('#666666')
                Text(this.isPlaying ? '播放中' : '已暂停')
                  .fontSize(14).fontColor(this.isPlaying ? '#6BCB77' : '#FF6B6B').fontWeight(FontWeight.Bold)
              }.margin({ bottom: 12 })

              Column() {
                if (this.isPlaying) {
                  Column() {
                    Text('▶ 播放中...').fontSize(18).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
                    Column() {
                      Row() {
                        Column() {}.layoutWeight(3).height(3).backgroundColor('#4D96FF').borderRadius(2)
                        Column() {}.layoutWeight(7).height(3).backgroundColor('#FFFFFF60').borderRadius(2)
                      }
                    }.width('80%').margin({ top: 12 })
                  }
                  .width('100%').height(160).backgroundColor('#333333').borderRadius(10).justifyContent(FlexAlign.Center)
                } else {
                  Column() {
                    Text('|| 已暂停').fontSize(18).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
                    Text('点击播放按钮开始').fontSize(12).fontColor('#FFFFFF80').margin({ top: 8 })
                  }
                  .width('100%').height(160).backgroundColor('#555555').borderRadius(10).justifyContent(FlexAlign.Center)
                }
              }

              Row({ space: 12 }) {
                Button(this.isPlaying ? '暂停' : '播放')
                  .onClick(() => { this.togglePlay() })
                  .fontSize(14).backgroundColor(this.isPlaying ? '#FF6B6B' : '#4D96FF')
                Button('停止')
                  .onClick(() => {
                    this.isPlaying = false
                  })
                  .fontSize(14).backgroundColor('#A0A0A0')
              }.margin({ top: 12 })
            }
            .backgroundColor(PANEL_BG_COLOR).padding(12).borderRadius(12)

            Column({ space: 6 }) {
              Text('播放统计').fontSize(14).fontColor(ACCENT_COLOR).fontWeight(FontWeight.Bold)
              Text(`播放次数: ${this.playCount}`).fontSize(13).fontColor('#333333')
              Text(`当前状态: ${this.isPlaying ? '播放' : '暂停'}`).fontSize(13).fontColor('#333333')
              Text('VideoController 管理播放状态')
                .fontSize(11).fontColor(MUTED_TEXT_COLOR).margin({ top: 4 })
              Text('回调: onStart / onPause / onStop / onFinish')
                .fontSize(11).fontColor(MUTED_TEXT_COLOR)
            }
            .width('100%').backgroundColor('#E8F4FD').padding(14).borderRadius(12)
          }.width('100%')
        }
      }
      Text('Video 播放状态 - 用状态同步播放器界面反馈')
        .fontSize(12).fontColor(MUTED_TEXT_COLOR).margin({ top: 12 })
    }
    .width('100%').height('100%').backgroundColor(PAGE_BG_COLOR).padding(16)
  }
}

togglePlay() 为什么是这篇最该看懂的方法

整篇代码里,最像“状态管理入口”的就是这个方法:

togglePlay(): void {
  this.isPlaying = !this.isPlaying
  if (this.isPlaying) {
    this.playCount++
  }
}

它做了两件事:

  1. 切换播放状态
  2. 在进入播放态时累加播放次数

这说明一个很重要的编程习惯:

一个用户动作,不一定只对应一个视觉变化,它还可能顺手更新统计信息。

这就是为什么实际项目里,状态入口最好集中管理,而不是分散在多个按钮里随手改。

为什么“播放状态”会影响这么多地方

在这份案例里,isPlaying 不只是决定按钮写“播放”还是“暂停”。它还同时影响:

  • 上方状态文字
  • 文字颜色
  • 播放器占位区样式
  • 播放提示文案
  • 按钮背景色
  • 底部状态统计

这就是状态驱动 UI 的典型特征。

页面不是“每一块都自己更新”,而是它们共同依赖同一个状态源。

条件渲染为什么很适合表达播放器状态差异

案例中这段很关键:

if (this.isPlaying) {
  ...播放中布局...
} else {
  ...暂停布局...
}

这比只换一行文字更有教学意义,因为它让你看到:

  • 播放中可以展示进度感
  • 暂停时可以展示静止提示

同一个容器,随着状态变化可以切换完全不同的内容结构。

playCount 为什么看起来小,却很有业务味

很多人会忽略这类计数状态,觉得不就是多记一个数字。

其实它很常见。

比如:

  • 用户点击播放多少次
  • 某段试看视频被触发了几次
  • 某个介绍视频是否真正被用户开始播放过

这些信息后面都可能用于:

  • 运营统计
  • 行为埋点
  • 试看逻辑控制
  • 用户学习进度分析

所以别小看 playCount 这种变量,它往往是从“演示代码”迈向“真实业务代码”的开始。

关键代码单独讲解

@State isPlaying: boolean = false

这是整页播放器 UI 的主状态来源。

Button(this.isPlaying ? '暂停' : '播放')

按钮文案直接跟状态绑定,避免出现“界面显示暂停,但其实已经暂停了”的割裂感。

this.playCount++

只有在切到播放态时才递增,逻辑上比每次点击都加一次更合理。

小白最容易踩的坑

1. 用按钮文案代替真实状态

这样很容易导致显示和实际行为不同步。

2. 停止按钮只改界面,不改状态源

结果 UI 看似停了,内部状态却没回到正确位置。

3. 所有状态逻辑都散落在按钮回调里

越写越多之后会很难维护。

这份代码能怎么改成真实业务

场景 改法
课程播放器 记录学习开始次数和暂停状态
产品视频页 根据播放状态切换提示文案和操作按钮
试看页 播放次数达到阈值后提示登录或开通会员
内容平台 结合真实 VideoController 同步组件状态

动手改一改

  • 给“停止”按钮补上进度归零逻辑。
  • 增加 isFinished 状态,区分播放结束和手动暂停。
  • 让页面记录最近一次播放发生的时间。
  • 把模拟播放器区替换成真实 Video 组件,再接控制器回调。

写在最后

播放器状态管理这件事,表面看像是切按钮,实质上是在组织界面和行为之间的关系。你能不能把状态源想清楚,直接决定后面的代码会不会乱。
如果你刚开始学 HarmonyOS7 的媒体组件,这篇值得多看两遍。先把播放、暂停、停止这些最基础的状态关系想明白,后面做全屏、倍速、进度同步时,心里会稳很多。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐