马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
概述
本文适用于视频播放类应用的开发,针对市场上主流视频播放类应用的常见场景,介绍了如何基于HarmonyOS能力快速实现视频播放应用。
从用户交互和音频流状态变更两个维度,指导开发者基于HarmonyOS提供的媒体和ArkUI等能力,实现视频前后台播放控制、播放形态切换、音频焦点切换、播放装备切换等场景,可以为视频播放应用提供机动的交互体验和良好的观看效果。
场景分析
场景名称 子场景名称 描述 实现方案 与用户交互 [播放进度控制] 进度条控制。 使用[Slider组件]实现进度条,在其onChange回调中触发进度调节。 手势调节播放进度。 通过给组件[绑定手势辨认],来实现手势调节播放进度。 显示进度条弹窗。 给进度条添加[Popup弹窗]。 [播放形态切换] 横竖屏切换。 基于[窗口能力]实现全屏播放。 悬浮窗播放。 [应用布局适配聪明多窗]。 [播控中心控制视频状态] 应用相应播控中心关照,向播控中心同步视频信息状态。 [监听AVSession播放状态事件]。 [视频后台播放] 应用至于后台时可以继续播放。 基于[AVSession Kit]和[Background Tasks Kit]申请长时任务。 [滑动调节音量及亮度] 视频全屏播放时,可以滑动屏幕调节音量及亮度。 使用[AVVolumePanel组件]控制音量,使用[Slider组件]及[setWindowBrightness]方法控制亮度。 音频流状态变更 [多音频并发打断] 视频应用被其他应用的音频打断做出对应的举动。 [AVPlayer监听音频打断事件]。 [播放装备切换] 视频应用毗连的播放装备状态发生变更时做出对应的举动。 [AVPlayer监听装备音频流输出变革]。 与用户交互
播放进度控制
进度条控制
进度条作为视频应用的一个根本能力,可以通过点击或拖动进度条来调节视频播放进度。采用[Slider组件]实现进度条功能,根据Slider组件属性设置进度条样式,并在其onChange()事件中触发视频播放器AVPlayer的seek()方法,实现视频进度的控制。
- Slider({
- value: this.isSliderGesture ? this.panEndTime : this.avPlayerController.currentTime,
- step: 0.1,
- min: 0,
- max: this.avPlayerController.durationTime,
- style: this.sliderStyle
- })
- // ...
- .trackColor($r('app.color.white_opacity_1_color')) // 滑轨背景颜色
- .showSteps(false) // 是否显示步长刻度
- .blockSize({ width: this.blockSize, height: this.blockSize }) // 滑块大小
- .blockColor($r('sys.color.background_primary')) // 滑块颜色
- .trackThickness(this.trackThicknessSize) // 滑轨粗细
- .trackBorderRadius(2) // 底板圆角半径
- .selectedBorderRadius(2) // 已滑动部分圆角半径
- // ...
- .onChange((value: number, mode: SliderChangeMode) => {
- this.sliderOnchange(value, mode); // 进度条变化接口
- })
- sliderOnchange(seconds: number, mode: SliderChangeMode) {
- let seekTime: number = seconds * this.avPlayerController.duration / this.avPlayerController.durationTime;
- this.currentStringTime = secondToTime(Math.floor(seekTime / 1000));
- this.avPlayerController.setCurrentStringTime(this.currentStringTime);
- switch (mode) {
- case SliderChangeMode.Begin:
- break;
- case SliderChangeMode.Click:
- break;
- case SliderChangeMode.Moving:
- // ...
- break;
- case SliderChangeMode.End:
- this.avPlayerController.seek(seekTime); // 调用AVPlayer的seek方法控制播放进度
- // ...
- break;
- default:
- break;
- }
- }
复制代码 手势调节播放进度
通常视频播放应用还可以支持手势滑动来调节播放的进度,可以给组件绑定手势辨认,来实现在视频界面左右滑动调节视频播放进度的能力,手势类型选择[PanGesture](平移手势)。
- onActionStart()阶段使用本地变量纪录当前视频播放的位置,并更新进度条状态变量,重新渲染UI界面;
- onActionUpdate()阶段根据滑动的隔断,盘算滑动后视频应跳转的播放位置,并渲染UI界面进度条动效;
- onActionEnd()阶段将终极盘算出的播放位置传给AVPlayer控制器,调用AVPlayer的seek()方法让视频跳转到指定播放位置,并更新进度条状态变量,重新渲染UI界面。
- build() {
- Stack({ alignContent: Alignment.BottomEnd }) {
- // ...
- XComponent({
- id: 'XComponent',
- type: XComponentType.SURFACE,
- controller: this.xComponentController
- })
- // ...
- }
- // ...
- .gesture(
- PanGesture({ direction: PanDirection.Horizontal })
- .onActionStart((event: GestureEvent) => {
- this.isSliderGesture = true;
- this.panStartX = event.offsetX;
- this.panStartTime = this.avPlayerController.currentTime;
- this.sliderOnchange(this.panStartTime, SliderChangeMode.Begin);
- })
- .onActionUpdate((event: GestureEvent) => {
- this.isSliderGesture = true;
- let panTime =
- this.panStartTime +
- (this.panStartX + event.offsetX) / this.slideWidth * this.avPlayerController.durationTime;
- this.panEndTime = Math.min(Math.max(0, panTime), this.avPlayerController.durationTime);
- this.sliderOnchange(this.panEndTime, SliderChangeMode.Moving);
- })
- .onActionEnd(() => {
- this.sliderOnchange(this.panEndTime, SliderChangeMode.End);
- this.isSliderGesture = false;
- })
- )
- }
复制代码 显示进度条弹窗
在正常欣赏视频的过程中,应用会记任命户的欣赏历史,当再次切换到原视频时,根据历史数据在进度条上以弹窗的情势显示相干信息。并且让弹窗跟随滑块位置移动,弹窗保留1秒后消散。同时历史数据的保留跟随视频组件的生命周期存亡。
由于Slider自带的showTips无法对弹窗样式进行自界说,只支持圆形气泡,且无法自界说控制弹窗的显示时长和出现机会。所以我们通过创建一个和Slider滑块巨细一致的透明Stack,并盘算滑块在屏幕中的位置,将盘算的位置数据同步设置给Stack,并给Stack绑定Popup弹窗跟随Slider滑块运动。
关键代码:
通过给Slider组件绑定地区变革事件onAreaChange(),盘算滑动位置信息。
- // Slider进度条
- Slider({
- value: this.isSliderGesture ? this.panEndTime : this.avPlayerController.currentTime,
- step: 0.1,
- min: 0,
- max: this.avPlayerController.durationTime,
- style: this.sliderStyle
- })
- // ...
- .blockSize({ width: this.blockSize, height: this.blockSize })
- // 计算滑块位置
- .onAreaChange(() => {
- let videoSlider: componentUtils.ComponentInfo = componentUtils.getRectangleById('video_slider')
- this.slideWidth = px2vp(videoSlider.size.width);
- // 计算offsetY:Slider滑块位置的纵坐标
- this.offsetY = px2vp(videoSlider.localOffset.y);
- this.beginX = px2vp(videoSlider.localOffset.x);
- })
- // 进度条变化时最终会调用avPlayer.seek()接口
- .onChange((value: number, mode: SliderChangeMode) => {
- this.sliderOnchange(value, mode);// 进度条变化时会调用avPlayer.seek()接口
- })
复制代码 Stack透明块巨细、位置设置以及给Stack绑定Popup弹窗。
- // 设置和Slider大小一样的透明stack块
- Stack() {
- }
- .backgroundColor($r('sys.color.background_primary'))
- .width(this.b
复制代码 |