Harmony OS 截图保存功能详解
·
一、引言
在 Harmony OS NEXT / 5.0 / API 12+ 版本的应用开发中,实现页面分享到相册的功能可以极大提升用户体验。本文将详细介绍如何通过 ArkTS 实现这一功能,从项目效果、思路分析、源码解读到核心要点总结,全方位带你了解其开发过程。
二、适用版本说明
本文所涉及的功能开发基于 Harmony OS NEXT / 5.0 / API 12+ 版本。该版本提供了丰富的 API 和框架支持,如 @kit.ArkUI、@kit.ImageKit、@ohos.file.fs 以及 @kit.MediaLibraryKit 等,为实现页面分享至相册功能奠定了坚实基础。
三、项目的效果

四、思路分析
- 捕获当前界面内容:利用
componentSnapshot.get('IdStr')捕获特定对话框的当前内容,生成一张图片(pixelMap),就像是给当前界面拍了一张 “照片”。 - 将图片压缩为可存储格式:借助
image.createImagePacker()创建图片打包器,把捕获的图片压缩成 JPEG 格式的二进制数据流,如同将 “照片” 进行优化处理,便于后续存储。 - 将图片写入临时文件:获取应用沙箱的缓存路径,创建临时文件。把压缩后的二进制数据流写入该文件并关闭文件句柄,这一步如同把优化后的 “照片” 存放在一个临时 “文件夹” 里。
- 将图片从临时文件移动到相册:获取临时文件的 URI 地址,通过
photoAccessHelper.MediaAssetChangeRequest将图片从应用私有空间转移到相册公共空间,就像把 “照片” 从临时 “文件夹” 移动到了手机相册这个 “公共相册” 中。提交变更请求,完成图片保存。 - 构建用户界面并处理交互:运用
Stack、Column和Row组件搭建对话框布局,包含二维码、分享文本和保存按钮。当用户点击保存按钮,触发saveImage方法保存图片并关闭对话框;若用户未授权,则提示授权失败。 - 提供操作反馈:在保存图片过程中,通过
promptAction.showToast向用户展示操作结果(成功或失败),让用户及时了解当前状态,如同给用户一个实时的 “小提示”。
五、源码详解
首页静态样式
import { buildDialog } from './buildDialog';
@Entry
@Component
struct Index {
dialog = new CustomDialogController({
builder: buildDialog(),
customStyle: true,
alignment: DialogAlignment.Center
});
build() {
Stack({ alignContent: Alignment.Center }) {
Column({ space: 30 }) {
Text('欢迎来到 ArkTS 页面')
.fontSize(36)
.fontWeight(FontWeight.Bold)
.fontColor('#333')
.margin({ top: 80 });
Text('这是一个简单的静态页面示例,为你展示基本的页面布局和分享功能。')
.fontSize(18)
.fontColor('#666')
.lineHeight(28)
.width('80%')
.textAlign(TextAlign.Center);
Button('分享页面')
.width(220)
.height(55)
.fontSize(20)
.fontWeight(FontWeight.Medium)
.backgroundColor('#007aff')
.fontColor(Color.White)
.borderRadius(28)
.shadow({
offsetX: 0,
offsetY: 4,
radius: 8,
color: '#007aff40'
})
.onClick(() => {
this.dialog.open();
});
}
.width('90%')
.padding({ top: 40, bottom: 40, left: 20, right: 20 })
.backgroundColor(Color.White)
.borderRadius(16)
.shadow({
offsetX: 0,
offsetY: 8,
radius: 24,
color: '#0000001a'
})
.alignItems(HorizontalAlign.Center);
}
.width('100%')
.height('100%')
.backgroundColor('#f4f4f9');
}
}
这段代码构建了应用的首页,是整个应用的 “入口”。
- 引入模块:导入
buildDialog,用于创建自定义弹窗。 - CustomDialogController:创建
dialog,配置自定义弹窗的构建器、样式和对齐方式。 - build 方法:
- 布局构建:通过
Stack和Column构建页面布局,展示欢迎信息、功能描述以及 “分享页面” 按钮。 - 按钮交互:点击 “分享页面” 按钮,打开自定义弹窗,为用户提供分享功能入口。
- 布局构建:通过
自定义弹窗页码(逻辑现实区域)
import { componentSnapshot, promptAction } from "@kit.ArkUI";
import { image } from "@kit.ImageKit";
import fileIo from "@ohos.file.fs";
import fileUri from "@ohos.file.fileuri";
import { photoAccessHelper } from "@kit.MediaLibraryKit";
@CustomDialog
export struct buildDialog {
controller: CustomDialogController;
private shareInfo: string = '这是一个待分享的精彩页面内容,快来看看吧!';
async saveImage() {
// 1. 根据组件的id生成截图对象
const pixelMap = await componentSnapshot.get('IdStr');
// 2. 借助ImagePacker去把图片对象生成二级制数据流
const imagePacker = image.createImagePacker();
// 图片是否压缩
const arrayBuffer = await imagePacker.packToData(pixelMap, { format: "image/jpeg", quality: 98 });
// 3. 借助fileIo读写文件
// 3.1 获取上下文
const ctx = getContext(this);
// 3.2 获取沙箱中存图的路径
const imagePath = ctx.cacheDir + '/' + Date.now() + '.jpeg';
// 3.3 以 创建 或 读写 的模式打开文件(没有则创建并打开, 有则打开)
const file = fileIo.openSync(imagePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE);
// 3.4 同步写入二级制数据流到文件中
fileIo.writeSync(file.fd, arrayBuffer);
// 3.5 同步去关闭文件
fileIo.closeSync(file.fd);
// 4. 把沙箱中的文件写入相册
// 4.1 获取资源文件的uri地址
const imgUrl = fileUri.getUriFromPath(imagePath);
// 4.2 进行图片资产变更(私有->公有)
const assetChangeRequest = photoAccessHelper.MediaAssetChangeRequest.createImageAssetRequest(ctx, imgUrl);
// 4.3 提交媒体变更请求
// 4.3.1 获取相册管理模块的实例
const phAccessHelper = photoAccessHelper.getPhotoAccessHelper(ctx);
// 4.3.2 调用变更方法
await phAccessHelper.applyChanges(assetChangeRequest);
promptAction.showToast({ message: '图片写入相册成功' });
}
build() {
Stack({ alignContent: Alignment.Center }) {
Column({ space: 30 }) {
QRCode('这里可以传(链接、参数)生成二维码')
.width(160)
.height(160)
.alignSelf(ItemAlign.Center);
Text(this.shareInfo)
.fontSize(18)
.fontColor('#666')
.lineHeight(28)
.width('80%')
.textAlign(TextAlign.Center)
.margin({ top: 50 });
Row({ space: 20 }) {
SaveButton({
icon: SaveIconStyle.FULL_FILLED,
text: SaveDescription.SAVE_IMAGE,
buttonType: ButtonType.Normal
})
.width(160)
.height(50)
.fontSize(18)
.fontWeight(FontWeight.Medium)
.backgroundColor('#007aff')
.fontColor(Color.White)
.borderRadius(25)
.onClick((event: ClickEvent, result: SaveButtonOnClickResult) => {
if (result === SaveButtonOnClickResult.SUCCESS) {
this.saveImage();
this.controller.close();
} else {
promptAction.showToast({ message: '授权失败' });
}
});
}
.margin({ bottom: 50 });
}
.width('90%')
.padding({ top: 40, bottom: 40, left: 20, right: 20 })
.backgroundColor(Color.White)
.borderRadius(16)
.alignItems(HorizontalAlign.Center);
}
.id('IdStr')
.width('100%')
.height('100%')
.backgroundColor('#f4f4f9');
}
}
此部分代码实现了自定义弹窗的逻辑和布局,是页面分享功能的核心区域。
- 引入模块:导入多个模块,包括用于组件快照的
@kit.ArkUI中的componentSnapshot和提示框的promptAction,用于图片处理的@kit.ImageKit,用于文件操作的@ohos.file.fs和@ohos.file.fileuri,以及用于相册管理的@kit.MediaLibraryKit中的photoAccessHelper。 - saveImage 方法:
- 截图生成:使用
componentSnapshot.get('IdStr')获取指定组件的截图,生成pixelMap。 - 图片压缩:通过
image.createImagePacker()创建图片打包器,并调用packToData方法将pixelMap压缩为 JPEG 格式的二进制数据流arrayBuffer。 - 文件操作:获取应用上下文,得到沙箱缓存路径,创建并打开临时文件,将二进制数据流同步写入文件后关闭文件。
- 相册写入:获取临时文件的 URI 地址,创建图片资产变更请求,通过相册管理模块实例提交变更请求,将图片保存到相册。最后通过提示框告知用户图片保存成功。
- 截图生成:使用
- build 方法:
- 布局构建:通过
Stack、Column和Row构建弹窗布局,包含二维码组件、分享文本和保存按钮。 - 按钮交互:点击保存按钮,根据点击结果执行相应操作。若授权成功,调用
saveImage方法保存图片并关闭弹窗;若授权失败,提示用户授权失败。
- 布局构建:通过
六、详细分析
- 根据组件 ID 生成截图对象:利用
componentSnapshot这个组件快照生成工具,通过get方法并传入唯一 ID(IdStr),为指定组件拍摄 “快照”,生成截图对象。 - 将生成的截图对象转换为二进制数据流:
- 创建图片编码器实例对象:调用
image.createImagePacker()创建图片编码器实例,就像准备好了一个专门处理图片的 “工具”。 - 配置并转换:调用实例的
packToData方法,传入截图对象pixelMap,并配置输出图片格式(format)为image/jpeg,压缩质量(quality)为 98,将截图对象转换为二进制数据流arrayBuffer。
- 创建图片编码器实例对象:调用
- 文件的读写操作:
- 获取上下文与路径:使用
getContext(this)获取上下文,进而得到沙箱缓存路径ctx.cacheDir,并结合当前时间生成唯一文件名,如ctx.cacheDir + '/' + Date.now() + '.jpeg'。 - 打开或创建文件:以
CREATE(创建文件)和READ_WRITE(读写权限)模式通过fileIo.openSync打开或创建文件,确保文件可用于存储截图。 - 写入和关闭文件:通过
fileIo.writeSync将二进制数据流写入文件,再用fileIo.closeSync关闭文件,完成文件操作。
- 获取上下文与路径:使用
- 调用权限与相册写入:
- 临时权限按钮:在项目中使用
SaveButton作为临时权限管理按钮。虽然方便,但在大型项目开发中,建议配置全局权限管理,并在调用时动态检查授权情况。 - 获取资源地址与变更请求:通过
fileUri.getUriFromPath获取存入沙箱的资源文件的 URI 地址,然后利用photoAccessHelper.MediaAssetChangeRequest.createImageAssetRequest创建图片资产变更请求,将私有文件转变为公有资产。 - 提交变更请求:获取相册管理模块实例
phAccessHelper,调用applyChanges方法提交媒体变更请求,将图片保存到相册。
- 临时权限按钮:在项目中使用
核心点总结
- 截图与压缩:依靠
componentSnapshot和image.createImagePacker()实现界面截图生成与图片压缩编码。 - 文件操作:运用
fileIo模块完成沙箱文件的创建、写入和关闭操作。 - 相册管理:借助
photoAccessHelper实现将图片从应用私有空间移动到相册公共空间。 - 用户交互:通过
SaveButton处理用户授权,并使用promptAction.showToast提供操作反馈。 - 二维码生成:使用
QRCode组件实现二维码的生成与展示。
核心实现步骤精要
1、动态截图与压缩(关键技术点):
// 捕获组件快照
const pixelMap = await componentSnapshot.get('IdStr');
// 压缩为 JPEG 二进制流
const imagePacker = image.createImagePacker();
const arrayBuffer = await imagePacker.packToData(pixelMap, {
format: "image/jpeg",
quality: 98 // 压缩质量(1 - 100)
});
2、沙箱文件操作(避坑指南):
// 获取沙箱缓存路径
const imagePath = ctx.cacheDir + '/' + Date.now() + '.jpeg';
// 同步写入文件(避免异步导致数据丢失)
const file = fileIo.openSync(imagePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE);
fileIo.writeSync(file.fd, arrayBuffer);
fileIo.closeSync(file.fd);
3、相册写入(系统级交互):
// 转换 URI 并提交媒体变更请求
const imgUrl = fileUri.getUriFromPath(imagePath);
const assetChangeRequest = photoAccessHelper.MediaAssetChangeRequest.createImageAssetRequest(ctx, imgUrl);
const phAccessHelper = photoAccessHelper.getPhotoAccessHelper(ctx);
await phAccessHelper.applyChanges(assetChangeRequest);
功能模块与技术方案总结
| 功能模块 | 技术方案 | 核心 API / 组件 |
|---|---|---|
| 界面截图生成 | 组件快照捕获 | componentSnapshot.get() |
| 图片压缩编码 | ImageKit 图片处理 | image.createImagePacker() |
| 文件存储 | 沙箱文件 I/O 操作 | fileIo.openSync() / fileIo.writeSync() |
| 相册写入 | 媒体库资源管理 | photoAccessHelper.MediaAssetChangeRequest |
| 用户交互 | 权限按钮与反馈 | SaveButton / promptAction.showToast() |
| 二维码生成 | 图形组件 | QRCode() |
七、总结
核心结论
通过本次开发实践,我们成功在 Harmony OS NEXT / 5.0 / API 12+ 版本上实现了页面分享至相册的功能。该功能综合运用了组件快照捕获、图片压缩处理、沙箱文件操作以及媒体库资源管理等多种技术,为用户提供了便捷的分享体验。
延伸学习
对于想要深入学习的开发者,可以进一步探索:
- 权限管理优化:在大型项目中,如何更好地配置全局权限管理,动态检查权限状态,提升应用的安全性和稳定性。
- 图片处理进阶:研究
ImageKit中更多的图片处理功能,如图片裁剪、添加水印等,丰富分享图片的内容和形式。 - 用户体验提升:优化界面设计,例如增加动画效果,让分享过程更加流畅和有趣,提升用户满意度。
关于 Harmony OS 开发的更多技巧和实践经验,我会在后续博客中持续分享,感兴趣的话欢迎关注。对于本文介绍的页面分享功能开发,你有什么疑问或者想法吗?欢迎随时交流。
更多推荐
所有评论(0)