文章目录
- 前言
- 这份案例在演示什么
- 完整代码
- 为什么 `pipEnabled` 代表的是一种任务模式切换
- 为什么主视频区和小窗要同时出现
- 小窗为什么适合用 `Stack` 做示意
- 说明区里的权限信息为什么不能忽略
- `.pictureInPicture(true/false)` 背后代表什么
- 关键代码单独讲解
- `Button(this.pipEnabled ? '关闭画中画' : '开启画中画')`
- `.position({ top: 8, right: 8 })`
- `.onClick(() => { this.pipEnabled = false })`
- 小白最容易踩的坑
- 1. 把画中画理解成普通弹层
- 2. 只做了小窗样式,没有想清楚主页面离开后的播放逻辑
- 3. 忽略权限配置
- 这份代码能怎么改成真实业务
- 动手改一改
- 写在最后
前言
很多视频能力都在提升“观看体验”,但画中画不太一样。它提升的是任务切换体验。
用户不一定总是愿意停留在播放器页里。看教程时可能想去记笔记,看会议回放时可能要顺手回消息,看课程时可能想一边查资料一边继续听。这时候,如果视频一离开当前页面就中断,体验会立刻打折。
这也是为什么画中画能力很有价值。它让播放不再被页面边界锁死。
这份案例在演示什么
页面核心围绕一个状态:
@StatepipEnabled:boolean=false它控制播放器是否进入画中画概念模式。
案例做了两件事:
- 展示主视频区域
- 在开启时叠加一个小窗播放器示意
这正好对应了画中画的核心认知:主内容可以让位,但播放不必停止。
完整代码
/** * VideoPictureInPicture - Video 画中画 * 演示 Video 画中画模式的概念和实现方式 */import{PRESET_COLORS,generateListItems,PAGE_BG_COLOR,PANEL_BG_COLOR,ACCENT_COLOR,MUTED_TEXT_COLOR}from'./types'@Entry@Componentstruct VideoPictureInPicture{@StateisShow:boolean=true@StatepipEnabled:boolean=falsebuild(){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.pipEnabled?'已开启':'未开启').fontSize(14).fontColor(this.pipEnabled?'#6BCB77':'#A0A0A0').fontWeight(FontWeight.Bold)}.margin({bottom:12})Column(){Column(){Text('主视频区域').fontSize(14).fontColor('#FFFFFF')}.width('100%').height(160).backgroundColor('#444444').borderRadius(10).justifyContent(FlexAlign.Center)if(this.pipEnabled){Stack(){Column(){Text('PiP').fontSize(10).fontColor('#FFFFFF')}.width(100).height(60).backgroundColor('#4D96FF').borderRadius(6).justifyContent(FlexAlign.Center)Text('×').fontSize(12).fontColor('#FFFFFF').width(20).height(20).backgroundColor('#FF6B6B').borderRadius(10).textAlign(TextAlign.Center).position({top:-5,right:-5})}.position({top:8,right:8}).onClick(()=>{this.pipEnabled=false})}}.width('100%').height(160)Button(this.pipEnabled?'关闭画中画':'开启画中画').onClick(()=>{this.pipEnabled=!this.pipEnabled}).fontSize(13).backgroundColor(this.pipEnabled?'#FF6B6B':ACCENT_COLOR).margin({top:10})}.backgroundColor(PANEL_BG_COLOR).padding(12).borderRadius(12)Column({space:6}){Text('画中画功能说明').fontSize(14).fontColor(ACCENT_COLOR).fontWeight(FontWeight.Bold)Text('Video 组件支持 pictureInPicture 模式').fontSize(12).fontColor('#333333')Text('开启后播放器以小窗悬浮在其他应用上').fontSize(12).fontColor('#333333')Text('用户可拖动小窗位置、调节大小').fontSize(12).fontColor('#333333')Text('切换应用时视频不中断播放').fontSize(12).fontColor('#333333')Text('API: .pictureInPicture(true/false)').fontSize(12).fontColor('#333333')Text('需在 module.json5 中配置 PiP 权限').fontSize(12).fontColor('#333333')Text('ohos.permission.PICTURE_IN_PICTURE').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)}}为什么pipEnabled代表的是一种任务模式切换
先看这行:
@StatepipEnabled:boolean=false它并不只是控制一个小窗显示不显示,而是在表达播放器是否进入“边播放边切任务”的模式。
这和全屏完全不同。
- 全屏强调沉浸
- 画中画强调不中断
所以它们虽然都属于视频高级能力,但服务的用户场景完全不一样。
为什么主视频区和小窗要同时出现
案例故意把两者放在一个结构里,是为了帮助你建立概念:
- 主视频区代表内容原本所在的位置
- 小窗代表播放能力被抽离出来,变成一个独立悬浮入口
你可以把画中画理解成“播放器脱离页面容器继续存在”。
小窗为什么适合用Stack做示意
看这段:
Stack(){小窗主体 关闭按钮}这很符合真实 PiP 的交互模型。一个小窗本体上面,往往还会附着:
- 关闭按钮
- 暂停按钮
- 恢复到主页面的入口
这类覆盖式控件结构,用Stack特别自然。
说明区里的权限信息为什么不能忽略
案例最后明确写了:
ohos.permission.PICTURE_IN_PICTURE这点非常重要。因为画中画不是纯前端视觉效果,它往往涉及系统级能力和权限配置。
也就是说,真正要落地 PiP,除了页面状态和组件调用外,你还得关心:
- 权限声明
- 系统支持情况
- 生命周期处理
- 小窗切换时的播放器状态同步
.pictureInPicture(true/false)背后代表什么
这行 API 方向:
.pictureInPicture(true/false)其实是在告诉你,画中画能力最终并不是靠“自己画个悬浮框”来实现,而是要借助系统提供的 PiP 模式。
教学案例先用状态和布局帮你理解概念,再对接真实 API,这个顺序是合理的。
关键代码单独讲解
Button(this.pipEnabled ? '关闭画中画' : '开启画中画')
按钮文案跟当前状态绑定,能让用户马上知道下一次点击会触发什么。
.position({ top: 8, right: 8 })
小窗放在右上角,是一种很常见的浮层示意位置,也方便和主画面形成明显区分。
.onClick(() => { this.pipEnabled = false })
小窗本身提供退出入口,这是 PiP 体验里很重要的一点。
小白最容易踩的坑
1. 把画中画理解成普通弹层
其实它更接近系统级播放延续能力。
2. 只做了小窗样式,没有想清楚主页面离开后的播放逻辑
这样做出来的只是视觉模拟,不是完整体验。
3. 忽略权限配置
页面代码写完了,但运行时根本进不了 PiP 模式。
这份代码能怎么改成真实业务
| 场景 | 改法 |
|---|---|
| 在线课程页 | 支持边看课程边记笔记 |
| 会议回放页 | 用户切到聊天或文档时继续听回放 |
| 直播回放页 | 允许用户边浏览其他内容边保留播放 |
| 教程应用 | 小窗播放时提供恢复到详情页入口 |
动手改一改
- 给小窗再补一个“返回主播放器”按钮。
- 进入画中画后隐藏主页面里不必要的播放控制区。
- 记录进入和退出 PiP 的状态变化。
- 在真实项目里补上
module.json5的权限配置。
写在最后
画中画模式最有价值的地方,不是界面看起来高级,而是它把播放从“页面功能”升级成了“跨页面连续能力”。这一步一旦做好,视频体验会立刻显得成熟很多。
如果你现在在做 HarmonyOS7 的媒体应用,这篇建议认真消化。因为它让你开始从页面视角之外,去理解播放器和系统能力之间的关系。