HarmonyOS ArkTS Video组件的使用(七)

news2025/4/12 12:14:22

概述

在手机、平板或是智慧屏这些终端设备上,媒体功能可以算作是我们最常用的场景之一。无论是实现音频的播放、录制、采集,还是视频的播放、切换、循环,亦或是相机的预览、拍照等功能,媒体组件都是必不可少的。以视频功能为例,在应用开发过程中,我们需要通过ArkUI提供的Video组件为应用增加基础的视频播放功能。借助Video组件,我们可以实现视频的播放功能并控制其播放状态。常见的视频播放场景包括观看网络上的较为流行的短视频,也包括查看我们存储在本地的视频内容。

Video组件用法介绍

Video组件参数介绍

Video组件的接口表达形式为:

Video(value: {src?: string | Resource, currentProgressRate?: number | string |PlaybackSpeed, previewUri?: string |PixelMap | Resource, controller?: VideoController})

其中包含四个可选参数,src、currentProgressRate、previewUri和controller。

  • src表示视频播放源的路径,可以支持本地视频路径和网络路径。使用网络地址时,如https,需要注意的是需要在module.json5文件中申请网络权限。在使用本地资源播放时,当使用本地视频地址我们可以使用媒体库管理模块medialibrary来查询公共媒体库中的视频文件,示例代码如下:
import mediaLibrary from '@ohos.multimedia.mediaLibrary';

async queryMediaVideo() {
  let option = {
    // 根据媒体类型检索
    selections: mediaLibrary.FileKey.MEDIA_TYPE + '=?',
    // 媒体类型为视频
    selectionArgs: [mediaLibrary.MediaType.VIDEO.toString()]
  };
  let media = mediaLibrary.getMediaLibrary(getContext(this));
  // 获取资源文件
  const fetchFileResult = await media.getFileAssets(option);
  // 以获取的第一个文件为例获取视频地址
  let fileAsset = await fetchFileResult.getFirstObject();
  this.source = fileAsset.uri
}

为了方便功能演示,示例中媒体资源需存放在resources下的rawfile文件夹里。

  • currentProgressRate表示视频播放倍速,其参数类型为number,取值支持0.75,1.0,1.25,1.75,2.0,默认值为1.0倍速;
  • previewUri表示视频未播放时的预览图片路径;
  • controller表示视频控制器。

参数的具体描述如下表:

在这里插入图片描述
下面我们通过具体的例子来说明参数的使用方法,我们选择播放本地视频,视频未播放时的预览图片路径也为本地,代码实现如下:

@Component
export struct VideoPlayer {
  private source: string | Resource;
  private controller: VideoController;
  private previewUris: Resource = $r('app.media.preview');
  ...

  build() {
    Column() {
      Video({
        src: this.source,
        previewUri: this.previewUris,
        controller: this.controller
      })
        ...
      VideoSlider({ controller: this.controller })
    }
  }
}

效果如下:

在这里插入图片描述

Video组件属性介绍

除了支持组件的尺寸设置、位置设置等通用属性外,Video组件还支持是否静音、是否自动播放、控制栏是否显示、视频显示模式以及单个视频是否循环播放五个私有属性。

在这里插入图片描述

其中,objectFit 中视频显示模式包括Contain、Cover、Auto、Fill、ScaleDown、None 6种模式,默认情况下使用ImageFit.Cover(保持宽高比进行缩小或者放大,使得图片两边都大于或等于显示边界),其他效果(如自适应显示、保持原有尺寸显示、不保持宽高比进行缩放等)可以根据具体使用场景/设备来进行选择。

在Codelab示例中体现了controls、autoplay和loop属性的配置,示例代码如下:

@Component
export struct VideoPlayer {
  private source: string | Resource;
  private controller: VideoController;
  ...
  build() {
    Column() {
      Video({
        controller: this.controller
      })
        .controls(false) //不显示控制栏 
        .autoPlay(false) // 手动点击播放 
        .loop(false) // 关闭循环播放 
        ...
    }
  }
}

Video组件回调事件介绍

Video组件能够支持常规的点击、触摸等通用事件,同时也支持onStart、onPause、onFinish、onError等事件,具体事件的功能描述见下表:

在这里插入图片描述
在Codelab中我们以更新事件、准备事件、失败事件以及点击事件为回调为例进行演示,代码实现如下:

Video({ ... })
  .onUpdate((event) => {
    this.currentTime = event.time;
    this.currentStringTime = changeSliderTime(this.currentTime); //更新事件 
  })
  .onPrepared((event) => {
    prepared.call(this, event); //准备事件 
  })
  .onError(() => {
    prompt.showToast({
      duration: COMMON_NUM_DURATION, //播放失败事件 
      message: MESSAGE
    });
  ...
  })

其中,onUpdate更新事件在播放进度变化时触发,从event中可以获取当前播放进度,从而更新进度条显示事件,比如视频播放时间从24秒更新到30秒。onError事件在视频播放失败时触发,在CommonConstants.ets中定义了常量类MESSAGE,所以在视频播放失败时会显示“请检查网络”。

const MESSAGE: string = '请检查网络'  

自定义控制器的组成与实现

自定义控制器的组成

Video组件的原生控制器样式相对固定,当我们对页面的布局色调的一致性有所要求,或者在拖动进度条的同时需要显示其百分比进度时,原生控制器就无法满足需要了。如下图右侧的效果需要使用自定义控制器实现,接下来我们看一下自定义控制器的组成。

在这里插入图片描述

为了实现自定义控制器的进度显示等功能,我们需要通过Row容器实现控制器的整体布局,然后借由Text组件来显示视频的播放起始时间、进度时间以及视频总时长,最后通过滑动进度条Slider组件来实现视频进度条的效果,代码如下:

@Component
export struct VideoSlider {
  ...
  build() {
    Row(...) {
      Image(...)
      Text(...)
      Slider(...)
      Text(...)
    }
    ...
  }
}

自定义控制器的实现

自定义控制器容器内嵌套了视频播放时间Text组件、滑动器Slider组件以及视频总时长Text组件 3个横向排列的组件,其中Text组件在之前的基础组件课程中已经有过详细介绍,这里就不再进行赘述。需要强调的是两个Text组件显示的时长是由Slider组件的onChange(callback: (value: number, mode: SliderChangeMode) => void)回调事件来进行传递的,而Text组件的数值与视频播放进度数值value则是通过@Provide与 @Consume装饰器进行的数据联动,实现效果可见图片下方黑色控制栏部分,具体代码步骤及代码如下:

获取/计算视频时长

export function prepared(event) {
  this.durationTime = event.duration;
  let second: number = event.duration % COMMON_NUM_MINUTE;
  let min: number = parseInt((event.duration / COMMON_NUM_MINUTE).toString());
  let head = min < COMMON_NUM_DOUBLE ? `${ZERO_STR}${min}` : min;
  let end = second < COMMON_NUM_DOUBLE ? `${ZERO_STR}${second}` : second;
  this.durationStringTime = `${head}${SPLIT}${end}`;
  ...
};

设置进度条参数及属性

Slider({
  value: this.currentTime,
  min: 0,
  max: this.durationTime,
  step: 1,
  style: SliderStyle.OutSet
})
  .blockColor($r('app.color.white'))
  .width(STRING_PERCENT.SLIDER_WITH)
  .trackColor(Color.Gray)
  .selectedColor($r('app.color.white'))
  .showSteps(true)
  .showTips(true)
  .trackThickness(this.isOpacity ? SMALL_TRACK_THICK_NESS : BIG_TRACK_THICK_NESS)
  .onChange((value: number, mode: SliderChangeMode) => {...})

计算当前进度播放时间及添加onUpdate回调

最后,在我们播放视频时还需要更新显示播放的时间进度,也就是左侧的Text组件。在视频开始播放前,播放时间默认为00:00,随着视频播放,时间需要不断更新为当前进度时间。所以左侧的Text组件我们不仅需要读取时间,还需要为其添加数据联动。这里,我们就是通过为Video组件添加onUpdate事件来实现的,在视频播放过程中会不断调用changeSliderTime方法获取当前的播放时间并进行计算及单位转化,从而不断刷新进度条的值,也就是控制器左侧的播放进度时间Text组件。

Video({...})
  ...
  .onUpdate((event) => {
    this.currentTime = event.time;
    this.currentStringTime = changeSliderTime(this.currentTime)
  }) 
export function changeSliderTime(value: number): string {
  let second: number = value % COMMON_NUM_MINUTE;
  let min: number = parseInt((value / COMMON_NUM_MINUTE).toString());
  let head = min < COMMON_NUM_DOUBLE ? `${ZERO_STR}${min}` : min;
  let end = second < COMMON_NUM_DOUBLE ? `${ZERO_STR}${second}` : second;
  let nowTime = `${head}${SPLIT}${end}`;
  return nowTime;
}; 

指定视频播放进度及添加onChange事件回调

如需手动进行进度条的拖动,则需要在Slider组件中指定播放进度,并为Slider组件添加onChange事件回调。Slider滑动时就会触发该事件回调,从而实现将视频定位到进度条当前刷新位置,完成时长组件渲染与视频播放进度数据联动。

Slider({...})
  .onChange((value: number, mode: SliderChangeMode) => {
    sliderOnchange.call(this, value, mode);
  })
export function sliderOnchange(value: number, mode: SliderChangeMode) {
  this.currentTime = parseInt(value.toString());
  this.controller.setCurrentTime(parseInt(value.toString()), SeekMode.Accurate);
  ...
};

到这里我们就实现了自定义控制器的构建,两个Text组件显示的时长是由Slider组件的onChange回调事件来进行传递的,而Text组件的数值与视频播放进度数值value则通过是onUpdate与onChange事件并借由@Provide @Consume装饰器进行的数据联动。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1240779.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

6-使用nacos作为注册中心

本文讲解项目中集成nacos&#xff0c;并将nacos作为注册中心使用的过程。本文不涉及nacos的原理。 1、项目简介 以一个演示项目为例&#xff0c;项目包含三个服务&#xff0c;调用及依赖如下图&#xff1a; 由图中可以看出&#xff0c;coupon-customer-serv为服务的消费者&a…

Python基础教程: sorted 函数

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 sorted 可以对所有可迭代的对象进行排序操作&#xff0c; sorted 方法返回的是一个新的 list&#xff0c;而不是在原来的基础上进行的操作。 从新排序列表。 &#x1f447; &#x1f447; &#x1f447; 更多精彩机密、教程…

9.4 Windows驱动开发:内核PE结构VA与FOA转换

本章将继续探索内核中解析PE文件的相关内容&#xff0c;PE文件中FOA与VA,RVA之间的转换也是很重要的&#xff0c;所谓的FOA是文件中的地址&#xff0c;VA则是内存装入后的虚拟地址&#xff0c;RVA是内存基址与当前地址的相对偏移&#xff0c;本章还是需要用到《内核解析PE结构导…

【论文阅读笔记】Emu Edit: Precise Image Editing via Recognition and Generation Tasks

【论文阅读笔记】Emu Edit: Precise Image Editing via Recognition and Generation Tasks 论文阅读笔记论文信息摘要背景方法结果额外 关键发现作者动机相关工作1. 使用输入和编辑图像的对齐和详细描述来执行特定的编辑2. 另一类图像编辑模型采用输入掩码作为附加输入 。3. 为…

第三节-Android10.0 Binder通信原理(三)-ServiceManager篇

1、概述 在Android中&#xff0c;系统提供的服务被包装成一个个系统级service&#xff0c;这些service往往会在设备启动之时添加进Android系统&#xff0c;当某个应用想要调用系统某个服务的功能时&#xff0c;往往是向系统发出请求&#xff0c;调用该服务的外部接口。在上一节…

Vue批量全局处理undefined和null转为““ 空字符串

我们在处理后台返回的信息&#xff0c;有的时候返回的是undefined或者null&#xff0c;这种字符串容易引起用户的误解&#xff0c;所以需要我们把这些字符串处理一下。 如果每个页面都单独处理&#xff0c;那么页面会很冗余&#xff0c;并且后期如果有修改容易遗漏&#xff0c…

生成式AI与大语言模型,东软已经准备就绪

伴随着ChatGPT的火爆全球&#xff0c;数以百计的大语言模型也争先恐后地加入了这一战局&#xff0c;掀起了一场轰轰烈烈的“百模大战”。毋庸置疑的是&#xff0c;继方兴未艾的人工智能普及大潮之后&#xff0c;生成式AI与大语言模型正在全球开启新一轮生产力革新的科技浪潮。 …

PostgreSQL (Hologres) 日期生成

PostgreSQL 生成指定日期下一个月的日期 &#xff08;在Hologres中&#xff0c;不支持递归查询&#xff09; SELECTto_char(T, YYYYMMDD)::int4 AS date_int,date(T) AS date_str,date_part(year, T)::int4 AS year_int,date_part(month, T)::int4 AS month_int,date_part(da…

中职组网络安全B模块-渗透提权2

任务五&#xff1a;渗透提权2 任务环境说明&#xff1a; 仅能获取xxx的IP地址 用户名&#xff1a;test&#xff0c;密码&#xff1a;123456 访问服务器主机&#xff0c;找到主机中管理员名称&#xff0c;将管理员名称作为Flag值提交&#xff1b; Flag:doyoudoyoudo 访问服…

使用Pytorch从零开始构建DCGAN

在本文中&#xff0c;我们将深入研究生成建模的世界&#xff0c;并使用流行的 PyTorch 框架探索 DCGAN&#xff08;生成对抗网络 (GAN) 的一种变体&#xff09;的实现。具体来说&#xff0c;我们将使用 CelebA 数据集&#xff08;名人面部图像的集合&#xff09;来生成逼真的合…

微服务学习|初识Docker、使用Docker、自定义镜像、DockerCompose、Docker镜像仓库

初识Docker 项目部署的问题 大型项目组件较多&#xff0c;运行环境也较为复杂&#xff0c;部署时会碰到一些问题 依赖关系复杂&#xff0c;容易出现兼容性问题 开发、测试、生产环境有差异 Docker如何解决依赖的兼容问题的? 将应用的Libs (函数库)、Deps (依赖)配置与应用…

c语言:用迭代法解决递归问题

题目&#xff1a; 解释&#xff1a;题目的意思就是用迭代法的空间和时间复杂的太高了&#xff0c;需要我们减小空间与时间的复杂度&#xff0c;我就想到了迭代法&#xff0c;思路和代码如下&#xff1a; #include <stdio.h> //这里是递归法转迭代法 int main() {int x,i…

Spark---转换算子、行动算子、持久化算子

一、转换算子和行动算子 1、Transformations转换算子 1&#xff09;、概念 Transformations类算子是一类算子&#xff08;函数&#xff09;叫做转换算子&#xff0c;如map、flatMap、reduceByKey等。Transformations算子是延迟执行&#xff0c;也叫懒加载执行。 2)、Transf…

Leetcode——121 买卖股票的最佳时机

(超时。。。。。。&#xff09;除了暴力法我是真的。。。。。。 class Solution {public int maxProfit(int[] prices) {int len prices.length;int max0;for(int i0;i<len-1;i){for(int ji1;j<len;j){int income prices[j] - prices[i];if(income>max){maxincome;…

路由的控制与转发原理

场景1&#xff1a;路由器收到数据包后&#xff0c;会根据数据包的目标IP地址&#xff0c;计算出目标网段&#xff0c;再确定终端设备的具体位置。这个过程中&#xff0c;还需要计算出接口&#xff0c;或数据包下一跳的地址。最终会生成一条路由&#xff0c;即路径&#xff0c;存…

外部中断为什么会误触发?

今天在写外部中断的程序的时候&#xff0c;发现中断特别容易受到干扰&#xff0c;我把手放在对应的中断引脚上&#xff0c;中断就一直触发&#xff0c;没有停过。经过一天的学习&#xff0c;找到了几个解决方法&#xff0c;所以写了这篇笔记。如果你的中断也时不时会误触发&…

基于JavaWeb+SSM+Vue教学辅助微信小程序系统的设计和实现

基于JavaWebSSMVue教学辅助微信小程序系统的设计和实现 源码获取入口前言主要技术系统设计功能截图Lun文目录订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码获取入口 前言 1.1 概述 随着信息时代的快速发展&#xff0c;互联网的优势和普及&#xff0c;人们生活…

二、防火墙-安全策略

学习防火墙之前&#xff0c;对路由交换应要有一定的认识 1、安全策略基本概念2、匹配顺序3、缺省包过滤4、安全策略发展史4.1.基于ACL包过滤4.2.融合UTM安全策略4.3.一体化安全策略 5、Local区域安全策略5.1.针对OSPF协议配置Local区域安全策略5.2.其他协议 6、ASPF6.1.帮助FTP…

PCIE链路训练-状态跳转1

A&#xff1a;12ms超时&#xff0c;或者再任何lane上检测到Electrical Idle Exit&#xff1b; B&#xff1a; 1.发送“receiver detection”之后没有一个lane的接收逻辑被rx检测到 2.不满足条件c&#xff0c;比如两次detection出现差别&#xff1b; C&#xff1a;发送端在没…

文本分析:NLP 魔法!

一、说明 这是一个关于 NLP 和分类项目的博客。NLP 是自然语言处理&#xff0c;目前需求量很大。让我们了解如何利用 NLP。我们将通过编码来理解流程和概念。我将在本博客中介绍 BagOfWords 和 n-gram 以及朴素贝叶斯分类模型。这个博客的独特之处&#xff08;这使得它很长&…