
回复
视频下载页面的功能主要提供了视频的播放,和下载,因为下载比较耗时,所以这里还用到了多线程taskpoll来实现。
@Local videoPlayerViewModel: VideoPlayerViewModel = new VideoPlayerViewModel()
aboutToAppear() {
// 获取传递过来的视频数据
const param = NavigationUtils.getInstance().getParamByIndex() as VideoData
if (param) {
this.videoPlayerViewModel.videoData = param
// 设置视频URL(优先使用小尺寸视频以提高流畅度)
this.videoPlayerViewModel.setUrl(param)
} else {
promptAction.showToast({
message: '未获取到视频数据',
duration: 2000
})
}
}
这里用到的视频的属性和事件比较多,需要单独讲解一下。
Video
组件,绑定视频资源 src
、视频控制器 controller
和预览图片 previewUri
。contain
,确保视频内容完整显示。onStart
:当视频开始播放时,将 isPlaying
状态设置为 true
。onPause
:当视频暂停时,将 isPlaying
状态设置为 false
。onFinish
:当视频播放结束时,将 isPlaying
状态设置为 false
,并将当前时间 currentTime
重置为 0
。onUpdate
:实时更新当前播放时间 currentTime
。onPrepared
:当视频准备就绪后,立即开始播放,并将 isPlaying
状态设置为 true
。Video({
src: this.videoPlayerViewModel.videoUrl,
controller: this.videoPlayerViewModel.controller,
previewUri: this.videoPlayerViewModel.videoData?.videos?.medium?.thumbnail
})
.width('100%')
.height(this.videoPlayerViewModel.isFullScreen ? '100%' : '240vp')
.objectFit(ImageFit.Contain)
.autoPlay(true)
.controls(true) // 使用系统默认控件
.onStart(() => {
this.videoPlayerViewModel.isPlaying = true
})
.onPause(() => {
this.videoPlayerViewModel.isPlaying = false
})
.onFinish(() => {
this.videoPlayerViewModel.isPlaying = false
this.videoPlayerViewModel.currentTime = 0
})
.onUpdate((event) => {
this.videoPlayerViewModel.currentTime = event.time
})
.onPrepared((event) => {
this.videoPlayerViewModel.duration = event.duration
// 视频准备好后立即开始播放
this.videoPlayerViewModel.controller.start()
this.videoPlayerViewModel.isPlaying = true
})
.enableAnalyzer(true)
.enableShortcutKey(true)
.onClick(() => {
if (this.videoPlayerViewModel.isPlaying) {
this.videoPlayerViewModel.controller.pause()
} else {
this.videoPlayerViewModel.controller.start()
}
})
视频信息结构就是常规的布局,这里就不体现核心代码了。
下载视频是这个项目中比较有意思的功能,因为是使用了多线程+下载的+保存到相册的能力,这里的主要逻辑是
主要逻辑:
request.agent.create
方法创建下载任务,并处理创建成功或失败的情况。task.start
方法开始下载,并处理下载开始、进度更新、下载完成和下载失败的情况。task.on('progress', ...)
事件更新下载进度。task.on('completed', ...)
事件,将视频文件保存到相册,并更新下载状态和进度。task.on('failed', ...)
事件和catch
块处理错误,并更新下载状态和进度。 // 下载视频
downFile() {
if (this.isDownloading) {
promptAction.showToast({ message: '已有下载任务进行中' });
return;
}
//上下文对象
let context = getContext(this) as common.UIAbilityContext
let filesDir = context.cacheDir
//保存到的沙箱路径
let filePath = `${filesDir}/${Date.now()}.mp4`;
this.filePath = filePath
//下载任务的配置信息
let config: request.agent.Config = {
action: request.agent.Action.DOWNLOAD,
url: this.videoUrl,
saveas: filePath,
gauge: true,
overwrite: true,
network: request.agent.Network.WIFI,
title: this.videoData?.tags.split(',')[0] || 'video_download' // 添加下载任务标题
}
//创建下载任务
request.agent.create(context, config)
.then(async (task: request.agent.Task) => {
this.isDownloading = true; // 开始下载
this.downloadProgress = 0; // 重置进度
//创建成功之后,开始下载任务
task.start((err: BusinessError) => {
if (err) {
request.agent.remove(task.tid)
promptAction.showToast({ message: '下载失败' })
this.isDownloading = false; // 下载失败
this.downloadProgress = 0;
} else {
promptAction.showToast({ message: '开始下载...' })
}
})
task.on('progress', (progressInfo: request.agent.Progress) => { // Explicitly type progressInfo
if (progressInfo.sizes[0] > 0) {
this.downloadProgress =
Math.floor((progressInfo.sizes[0] / progressInfo.sizes[0]) * 100);
}
})
task.on('completed', async () => {
//下载完成--->保存到相册
this.isDownloading = false; // 下载完成
this.downloadProgress = 100; // 确保进度为100%
promptAction.showToast({ message: '下载完成,正在保存到相册...' });
this.save(filePath)
// 可以在这里稍作延迟后重置进度条,或者在保存成功后
setTimeout(() => {
this.downloadProgress = 0;
}, 2000);
})
task.on('failed', () => {
promptAction.showToast({ message: '下载任务失败' });
this.isDownloading = false;
this.downloadProgress = 0;
})
})
.catch((err: BusinessError) => {
promptAction.showToast({ message: `创建下载任务失败: ${err.message}` });
this.isDownloading = false;
this.downloadProgress = 0;
})
}
该功能在之前的教程中已经出现过了,
context
)。photoAccessHelper
对象,用于访问和管理设备的照片和视频库。srcFileUris
,这里只包含一个URI,即要保存的视频文件。photoCreationConfigs
数组,其中包含了保存视频时的配置信息,如文件名后缀、照片类型(这里是视频)、标题和子类型等。showAssetsCreationDialog
方法,弹出授权对话框,让用户选择保存视频的目标位置,并返回目标URI。fileIo.open
方法以读写模式打开源文件和目标文件。fileIo.copyFile
方法将源文件的内容复制到目标文件中。 // 保存到相册
async save(srcFileUri: string) {
let context = getContext(this)
let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
try {
let srcFileUris: Array<string> = [
srcFileUri
];
// 指定待保存照片的创建选项,包括文件后缀和照片类型,标题和照片子类型可选
let photoCreationConfigs: Array<photoAccessHelper.PhotoCreationConfig> = [
{
title: `${Date.now()}`, // 可选
fileNameExtension: 'mp4',
photoType: photoAccessHelper.PhotoType.VIDEO,
subtype: photoAccessHelper.PhotoSubtype.DEFAULT, // 可选
}
];
// 基于弹窗授权的方式获取媒体库的目标uri
let desFileUris: Array<string> = await phAccessHelper.showAssetsCreationDialog(srcFileUris, photoCreationConfigs);
// 将来源于应用沙箱的内容写入媒体库的目标uri
let desFile: fileIo.File = await fileIo.open(desFileUris[0], fileIo.OpenMode.WRITE_ONLY);
let srcFile: fileIo.File = await fileIo.open(srcFileUri, fileIo.OpenMode.READ_ONLY);
await fileIo.copyFile(srcFile.fd, desFile.fd);
fileIo.closeSync(srcFile);
fileIo.closeSync(desFile);
promptAction.showToast({ message: '保存成功' })
} catch (error) {
promptAction.showToast({ message: '保存失败' })
}
}
获取资料的途径,可以关注我们 官网的公众号 青蓝逐码 ,输入 项目名称 《自然壁纸》 即可获得以上资料。
如果你兴趣想要了解更多的鸿蒙应用开发细节和最新资讯,甚至你想要做出一款属于自己的应用!欢迎在评论区留言或者私信或者看我个人信息,可以加入技术交流群。