HarmonyOS NEXT边学边玩:从零实现一个影视App(六、视频播放页的实现)
在HarmonyOS NEXT中,ArkUI是一个非常强大的UI框架,能够帮助开发者快速构建出美观且功能丰富的用户界面。本文将详细介绍如何使用ArkUI实现一个影视App的视频播放页面。将从零开始,逐步构建一个功能完善的视频播放页面,并解释每一部分的代码实现。
1. 项目结构
在开始之前,先来看一下项目的结构。我的项目结构如下:
src/
├── common/
│ ├── bean/
│ │ └── ApiTypes.ts
│ └── dialog/
│ └── EpisodeDialogView.ts
├── pages/
│ └── VideoPlayerPage.ts
ApiTypes.ts
定义了视频数据的类型。EpisodeDialogView.ts
是一个自定义的剧集选择对话框组件。VideoPlayerPage.ts
是视频播放页面的主文件。
2. 视频播放页面的实现
2.1 引入必要的模块
首先,需要引入一些必要的模块和组件:
import { VideoItem } from "../../common/bean/ApiTypes";
import { window } from "@kit.ArkUI";
import { common } from "@kit.AbilityKit";
import { EpisodeDialogView } from "../../common/dialog/EpisodeDialogView";
VideoItem
是定义的一个视频数据类型的接口。window
和common
是HarmonyOS提供的系统模块,用于控制窗口和UI能力。EpisodeDialogView
是自定义的剧集选择对话框组件。
2.2 构建视频播放页面
使用 @Builder
装饰器来构建视频播放页面:
@Builder
export function VideoPlayerPageBuilder() {VideoPlayer()
}
VideoPlayerPageBuilder
是一个构建函数,它调用了 VideoPlayer
组件来构建整个页面。
2.3 视频播放组件
接下来,定义 VideoPlayer
组件:
@Component
struct VideoPlayer {@State title: string = '';private controller: VideoController | undefined;@State previewUri: Resource = $r('app.media.play_circle_fill');@State videoSrc: string = 'http://staticvip.iyuba.cn/video/small/202412/1009544_c.mp4'; // 使用时请替换为实际视频加载网址@State tvUrls?: string[] = [];@State isTv: boolean = false;@State tvIndex: number = 0;private description: string = '';private rawTitle: string = '';private isToggle = false;@State toggleText: string = '';@State toggleBtn: string = '展开';
@State
是ArkUI中的状态管理装饰器,用于管理组件的状态。controller
是视频播放器的控制器,用于控制视频的播放、暂停等操作。previewUri
是视频的预览图资源。videoSrc
是视频的播放地址。tvUrls
是电视剧的剧集列表。isTv
用于判断当前播放的是否是电视剧。tvIndex
是当前播放的剧集索引。description
是视频的简介。rawTitle
是视频的原始标题。isToggle
用于控制简介的展开和收起状态。toggleText
是简介的显示文本。toggleBtn
是简介展开/收起按钮的文本。
2.4 页面生命周期
在 aboutToAppear
生命周期函数中,可以进行一些初始化操作:
aboutToAppear() {// 初始化操作
}
2.5 屏幕方向切换
定义了一个 changeOrientation
方法,用于切换屏幕方向:
private changeOrientation(isLandscape: boolean) {let context = getContext(this) as common.UIAbilityContext;window.getLastWindow(context).then((lastWindow) => {lastWindow.setPreferredOrientation(isLandscape ? window.Orientation.LANDSCAPE : window.Orientation.PORTRAIT);});
}
getContext
用于获取当前组件的上下文。window.getLastWindow
获取当前窗口,并设置其方向为横屏或竖屏。
2.6 剧集选择对话框
定义了一个剧集选择对话框,并为其设置了回调函数:
episodeDialogController: CustomDialogController = new CustomDialogController({builder: EpisodeDialogView({current: this.tvIndex,tvUrls: this.tvUrls,clickCallback: (item: string, index: number) => { this.onDialogClickCallback(item, index) }}),alignment: DialogAlignment.Bottom,offset: { dx: 0, dy: -30 },customStyle: true
});private onDialogClickCallback(item: string, index: number) {this.videoSrc = item;this.tvIndex = index;this.title = this.rawTitle + ' 第' + (index + 1) + '集';this.episodeDialogController.close();this.controller?.start();
}
CustomDialogController
是自定义对话框的控制器。EpisodeDialogView
是剧集选择对话框的视图组件。onDialogClickCallback
是剧集选择后的回调函数,用于更新视频播放地址和标题。
剧集选择框的实现:
/*** @author: 猫哥* @date: 2025/1/15 0:30* @description:* @version:*/
/*** 电视剧集数选择对话框*/
@Preview
@CustomDialog
export struct EpisodeDialogView {controller: CustomDialogController@Prop current: number@Prop tvUrls?:string[] = []//确认按钮回调clickCallback?: (item:string,idx:number) => void// 添加一个方法来获取按钮的背景颜色private getButtonColor(idx: number): Color {if (this.current === idx) {return Color.Blue // 当前选中的按钮背景色为蓝色} else {return Color.White // 其他按钮背景色为白色}}build() {Column({space: 10}){Grid() {ForEach(this.tvUrls, (item: string,idx) => {GridItem() {Button('第'+(idx+1)+'集', { buttonStyle: ButtonStyleMode.NORMAL, role: ButtonRole.NORMAL }).borderRadius(5).fontColor('#fffab52a').width(80).height(40).padding(5).backgroundColor(this.getButtonColor(idx)).onClick(() => {this.clickCallback?.(item,idx)})}}, (item: string) => item.toString())}.width('100%').scrollBar(BarState.Off).columnsTemplate('1fr 1fr 1fr 1fr').columnsGap(4).rowsGap(4).height(300)}.width('98%').height('40%').backgroundColor(Color.White).padding(20).borderRadius(20)}
}
2.7 按钮背景颜色
定义了一个方法来获取按钮的背景颜色:
private getButtonColor(idx: number): Color {if (this.tvIndex === idx) {return Color.Blue; // 当前选中的按钮背景色为蓝色} else {return Color.White; // 其他按钮背景色为白色}
}
2.8 构建页面布局
最后,使用 build
方法来构建页面的布局:
build() {NavDestination() {Column() {Row() {Stack() {Video({src: this.videoSrc,previewUri: this.previewUri,controller: this.controller}).width('100%').muted(false) //设置是否静音.controls(true) //设置是否显示默认控制条.autoPlay(true) //设置是否自动播放.loop(false) //设置是否循环播放.objectFit(ImageFit.Contain) //设置视频适配模式.onError(() => { //失败事件回调console.info("Video error.");}).onFullscreenChange(event => {if (event.fullscreen) {this.changeOrientation(true);} else {this.changeOrientation(false);}}).zIndex(1);Text(this.title).fontColor(Color.White).width('100%').padding(5).alignSelf(ItemAlign.Start).margin({ bottom: 280 }).zIndex(2);}}.width('100%').height('40%');Column() {Text('简介').fontSize(18).padding({ bottom: 10 }).fontWeight(FontWeight.Bold).alignSelf(ItemAlign.Start);Text(this.toggleText).fontSize(14).lineHeight(20).alignSelf(ItemAlign.Start);Text(this.toggleBtn).fontSize(14).fontColor(Color.Gray).padding(10).alignSelf(ItemAlign.End).onClick(() => {this.isToggle = !this.isToggle;if (this.isToggle) {this.toggleBtn = '收起';this.toggleText = this.description;} else {this.toggleBtn = '展开';this.toggleText = this.description.substring(0, 100) + '...';}});}.padding(10);if (this.isTv) {Flex({direction: FlexDirection.Row,justifyContent: FlexAlign.SpaceBetween,alignContent: FlexAlign.SpaceBetween}) {Text('选集').fontSize(18).fontWeight(FontWeight.Bold);SymbolGlyph($r('sys.symbol.more')).fontWeight(FontWeight.Lighter).fontSize(32).fontColor(['#fffab52a']).onClick(() => {this.episodeDialogController.open();});}.padding(10);Scroll() {Row({ space: 10 }) {ForEach(this.tvUrls, (item: string, idx) => {Button('第' + (idx + 1) + '集', { buttonStyle: ButtonStyleMode.NORMAL, role: ButtonRole.NORMAL }).borderRadius(5).fontColor('#fffab52a').width(80).height(40).padding(5).backgroundColor(this.getButtonColor(idx)).onClick(() => {this.videoSrc = item;this.tvIndex = idx;this.title = this.rawTitle + ' 第' + (idx + 1) + '集';this.controller?.start();});});}.alignItems(VerticalAlign.Center).padding(10);}.scrollable(ScrollDirection.Horizontal);}}.width('100%');}.width("100%").height("100%").onReady(ctx => {interface params {item: VideoItem;}let par = ctx.pathInfo.param as params;this.videoSrc = par.item.video;this.rawTitle = par.item.title;this.title = this.rawTitle;this.description = par.item.desc;this.tvUrls = par.item.tvurls;if (this.tvUrls?.length ?? 0 > 0) {this.isTv = true;}this.tvIndex = 0;this.toggleText = this.description.substring(0, 100) + '...';}).onShown(() => {console.info('VideoPlayer onShown');});
}
NavDestination
是导航目的地组件,用于定义页面的导航行为。Column
和Row
是布局组件,用于构建页面的布局。Video
是视频播放组件,用于播放视频。Text
是文本组件,用于显示标题和简介。Button
是按钮组件,用于选择剧集。Scroll
是滚动组件,用于实现剧集列表的横向滚动。
3. 总结
通过以上步骤,实现了一个功能完善的视频播放页面。这个页面不仅支持视频的播放、暂停、全屏等基本功能,还支持电视剧的剧集选择和简介的展开/收起。希望这篇文章能够帮助你更好地理解如何使用HarmonyOS NEXT和ArkUI来构建一个影视App的视频播放页面。
如果你有任何问题或建议,欢迎在评论区留言!
作者介绍
作者:csdn猫哥
原文链接:https://blog.csdn.net/yyz_1987
团队介绍
坚果派团队由坚果等人创建,团队拥有12个华为HDE带领热爱HarmonyOS/OpenHarmony的开发者,以及若干其他领域的三十余位万粉博主运营。专注于分享HarmonyOS/OpenHarmony、ArkUI-X、元服务、仓颉等相关内容,团队成员聚集在北京、上海、南京、深圳、广州、宁夏等地,目前已开发鸿蒙原生应用和三方库60+,欢迎交流。
版权声明
本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
相关文章:

HarmonyOS NEXT边学边玩:从零实现一个影视App(六、视频播放页的实现)
在HarmonyOS NEXT中,ArkUI是一个非常强大的UI框架,能够帮助开发者快速构建出美观且功能丰富的用户界面。本文将详细介绍如何使用ArkUI实现一个影视App的视频播放页面。将从零开始,逐步构建一个功能完善的视频播放页面,并解释每一部…...

salesforce实现一个字段的默认初始值根据另一个字段的值来自动确定
在 Salesforce 中,可以通过 公式字段 或 触发器 (Trigger) 实现字段的默认初始值根据另一个字段的值来自动确定,具体实现方法如下: 1. 使用公式字段 公式字段是一种动态字段,值会根据公式实时计算。 步骤: 导航到字段…...

Linux 文件权限详解
目录 前言 查看文件权限 修改文件权限 符号方式 数字方式 前言 Linux 文件权限是系统中非常重要的概念之一,用于控制对文件和目录的访问。权限分为读(Read)、写(Write)、执行(Execute)三个…...

【混合开发】CefSharp+Vue桌面应用程序开发
为什么选择CefSharpVue做桌面应用程序 CefSharp 基于 Chromium Embedded Framework (CEF) ,它可以将 Chromium 浏览器的功能嵌入到 .NET 应用程序中。通过 CefSharp,开发者可以在桌面应用程序中集成 Web 技术,包括 HTML、JavaScript、CSS 等…...

springBoot项目使用Elasticsearch教程
目录 一、引言(一)使用背景(二)版本库区别 二、引入依赖(一)springboot集成的es依赖(建议)(二)es提供的客户端库 三、配置(以yaml文件为例&#x…...

模型 多元化思维(系统科学)
系列文章分享模型,了解更多👉 模型_思维模型目录。融合多学科知识,全面解决问题。 1 多元化思维模型的应用 1.1 完美日记的私域流量运营 完美日记作为美妆行业的新兴品牌,通过多元化的思维模型在私域流量运营中取得了显著成功。…...

Google地图瓦片爬虫
地图地址说明 1、谷歌矢量(中文标注) http://mt{0-3}.google.cn/vt/vm416115521&hlzh-CN&glcn&x{x}&y{y}&z{z}&sGalileo 2、谷歌矢量(英文标注) http://mt{0-3}.google.cn/vt/vm416115521&hlen&glcn&x{x}&y{y}&z{z}&sGali…...

【C++】size_t全面解析与深入拓展
博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 💯前言💯一、什么是size_t?为什么需要size_t? 💯二、size_t的特性与用途1. size_t是无符号类型示例: 2. size_t的跨平台适应性示例对…...

Web端实时播放RTSP视频流(监控)
一、安装ffmpeg: 1、官网下载FFmpeg: Download FFmpeg 2、点击Windows图标,选第一个:Windows builds from gyan.dev 3、跳转到下载页面: 4、下载后放到合适的位置,不用安装,解压即可: 5、配置path 复制解压后的\bin路径,配置环境变量如图: <...

学习 Git 的工作原理,而不仅仅是命令
Git 是常用的去中心化源代码存储库。它是由 Linux 创建者 Linus Torvalds 创建的,用于管理 Linux 内核源代码。像 GitHub 这样的整个服务都是基于它的。因此,如果您想在 Linux 世界中进行编程或将 IBM 的 DevOps Services 与 Git 结合使用,那…...

C语言变长嵌套数组常量初始化定义技巧
有时候,我们需要在代码里配置一些常量结构,比如一个固定的动作流程ActionFlow:包含N(即flow_num)个动作列表(ActionArray),每个动作列表包含M(即act_num)个可…...

如何查看特定版本的Spring源码
写在前面:大家好!我是晴空๓。如果博客中有不足或者的错误的地方欢迎在评论区或者私信我指正,感谢大家的不吝赐教。我的唯一博客更新地址是:https://ac-fun.blog.csdn.net/。非常感谢大家的支持。一起加油,冲鸭&#x…...

【深度学习】关键技术-激活函数(Activation Functions)
激活函数(Activation Functions) 激活函数是神经网络的重要组成部分,它的作用是将神经元的输入信号映射到输出信号,同时引入非线性特性,使神经网络能够处理复杂问题。以下是常见激活函数的种类、公式、图形特点及其应…...

网关相关知识
文章目录 什么是网关网关的主要作用网关的运用 什么是网关 网关又称网间连接器、协议转换器,也就是网段(局域网、广域网)关卡,不同网段中的主机不能直接通信,需要通过关卡才能进行互访,比如IP地址为192.168.31.9(子网掩码&#x…...

SpringBoot整合SpringSecurity详解
文章目录 SpringBoot整合SpringSecurity详解一、引言二、引入依赖三、配置 Spring Security四、自定义用户详细信息服务五、使用示例1. 创建用户实体类2. 测试登录功能 六、总结 SpringBoot整合SpringSecurity详解 一、引言 在当今的软件开发中,安全是一个至关重要…...

【C++基础】enum,union,uint8_t,static
enum 所以有时候使用 Enum 的目的,不是为了自定义一种数据类型,而是为了声明一组常量。 from: https://github.com/wangdoc/clang-tutorial/blob/main/docs/enum.md union C 语言提供了 Union 结构,用来自定义可以灵活变更的数据结构。它内部…...

单片机的原理及其应用:从入门到进阶的全方位指南
以下是一篇详细、深入的“单片机的原理及其应用”博客文章示例,适合想要系统学习或深入了解单片机的读者。文中不仅会介绍单片机的基本原理、内部构造、开发流程和应用领域,还会融入更多的理论分析、实操案例以及常见问题与解决思路等,帮助读…...

如何使用 Go语言操作亚马逊 S3 对象云存储
以下是使用 Go 语言操作亚马逊 S3 对象云存储的详细步骤和示例代码: 解决思路: 安装必要的 Go 语言包,这里我们将使用 aws-sdk-go 包来与 Amazon S3 进行交互。配置 AWS 凭证,包括访问密钥和秘密访问密钥,以及 AWS 区…...

2025年应用与API安全展望:挑战与机遇并存
进入2025年,应用与API安全的重要性愈发突出。在过去的一年里,API技术已经成为数字创新的核心。然而,API的大规模应用也使得攻击面显著扩展,2024年针对业务逻辑漏洞的API攻击占比高达27%,较前一年增加10%。与此同时&…...

Linux安装docker,安装配置xrdp远程桌面
Linux安装docker,安装配置xrdp远程桌面。 1、卸载旧版本docker 卸载旧版本docker命令 yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine现在就是没有旧版本的d…...

VD:生成a2l文件
目录 前言Simulink合并地址 ASAP2 editor 前言 我之前的方法都是通过Simulink模型生成代码的过程中顺便就把a2l文件生成出来了,这时的a2l文件还没有地址,所以紧接着会去通过elf文件更新地址,一直以为这是固定的流程和方法,今天无…...

【SpringBoot应用篇】SpringBoot+MDC+自定义Filter操作traceId实现日志链路追踪
【SpringBoot应用篇】SpringBootMDC自定义Filter操作traceId实现日志链路追踪 解决的问题解决方案MDC具体逻辑ymllogback-spring.xmlTraceIdUtil操作工具类TraceIdFilter自定义过滤器GlobalExceptionHandler全局异常处理类TraceIdAspect切面UserController测试验证 多线程处理M…...

unity2022以上导出到AndroidStudio后更新步骤
1、unity里面Export出unityLibrary 2、导出apk,里面才包含libil2cpp(新版unity无法直接导出libil2cpp 3、注释AS项目app下的build.gradle里面包含unityLibrary的代码 4、注释AS项目settings.gradle包含unityLibrary的代码 5、删除AS项目里面的unityLibrary文件夹 6、…...

【ArcGIS初学】产生随机点计算混淆矩阵
混淆矩阵:用于比较分类结果和地表真实信息 总体精度(overall accuracy) :指对角线上所有样本的像元数(正确分类的像元数)除以所有像元数。 生产者精度(producers accuracy) :某类中正确分类的像元数除以参考数据中该类的像元数(列方向),又称…...

Harmony面试模版
1. 自我介绍 看表达能力、沟通能力 面试记录: 2. 进一步挖掘 2.1. 现状 目前是在职还是离职,如果离职,从上一家公司离职的原因 2.2. 项目经验 如果自我介绍工作项目经验讲的不够清楚,可以根据简历上的信息再进一步了解 面试记…...

PCM5142集成32位384kHz PCM音频立体声114dB差分输出DAC编解码芯片
目录 PCM5142 简介PCM5142功能框图PCM5142特性 参考原理图 PCM5142 简介 PCM514x 属于单片 CMOS 集成电路系列,由立体声数模转换器 (DAC) 和采用薄型小外形尺寸 (TSSOP) 封装的附加支持电路组成。PCM514x 使用 TI 最新一代高级分段 DAC 架构产品,可实现…...

浪潮云财务系统xtdysrv.asmx存在命令执行漏洞
一、漏洞简介 浪潮云财务系统xtdysrv.asmx存在命令执行漏洞,未经身份验证的远程攻击者可通过该漏洞在服务器端任意执行代码。 二、漏洞影响 浪潮云财务系统三、网络测绘: fofa: title"TSCEV4.0"四、复现过程 前置条件 步骤 POC 1 数据包…...

【网络编程】基础知识
目录 网络发展史 局域网和广域网 局域网(LAN) 广域网(Wan) 光猫 路由器 网线 设备通信的要素 IP地址 基本概念 地址划分 特殊地址(后续编程使用) IP地址转换 字节序 网络模型 网络的体系结…...

ResNet (Residual Network) - 残差网络:深度卷积神经网络的突破
一、引言 在计算机视觉领域,图像识别一直是一个核心且具有挑战性的任务。随着深度学习的发展,卷积神经网络(CNN)在图像识别方面取得了显著的成果。然而,随着网络深度的增加,出现了梯度消失或梯度爆炸等问题…...

MOSFET体二极管的反向恢复分析
1、MOSFET体二极管的反向恢复分析 MOSFET体二极管反向恢复会导致MOSFET工作时超出安全工作区(SOA),并引发其他电磁干扰问题(EMI。 二极管在反向恢复过程中会产生比较大的损耗。在正向偏置状态下,大量的电子和空穴载流…...