【案例实战】HarmonyOS图片处理应用开发实战
本文介绍基于HarmonyOS 5.0.2开发的全功能图片编辑器"可可图片编辑",涵盖图片压缩、裁剪、滤镜等核心功能,并前瞻性集成HarmonyOS 6.0特性。文章详细讲解了项目架构设计、技术栈选型,以及图片压缩功能的具体实现流程,包括图片选择、压缩处理、图库保存和分享功能。通过ImagePacker和PhotoAccessHelper等关键API,开发者可快速构建高效图片处
HarmonyOS图片处理应用开发实战:从零构建全功能图片编辑器
前言
随着移动互联网的快速发展,图片处理已成为用户日常高频使用的功能之一。本文将基于HarmonyOS 5.0.2平台,从零开始构建一款功能完善的图片处理应用——可可图片编辑。该应用涵盖图片压缩、裁剪、滤镜美化、水印添加等核心功能,通过本实战项目,您将全面掌握HarmonyOS图像处理的关键技术和最佳实践。
值得一提的是,本项目在5.0.2版本基础上,已经前瞻性地集成了部分HarmonyOS 6.0的新特性。如果您使用的是HarmonyOS 6.0设备,可以直接体验到更加流畅的性能表现和更加丰富的系统能力,充分感受鸿蒙生态的持续演进。
项目已上架华为应用市场,读者可扫码体验:

扫码体验

- 应用市场地址:https://appgallery.huawei.com/app/detail?id=tupianbmjidashi.qinglanzhuma.huawei
一、项目整体架构设计
1.1 技术栈选型
本项目采用以下技术方案:
- 开发环境:DevEco Studio
- SDK版本:HarmonyOS 5.0.2(14)
- 路由方案:ZRouter
- 状态管理:V2架构
- UI框架:ArkUI声明式开发范式
1.2 核心功能模块
可可图片编辑提供以下核心能力:
- 图片压缩:高效压缩图片大小,节省存储空间
- 精准裁剪:自定义裁剪区域,满足多样化需求
- 滤镜美化:多种滤镜效果,一键提升图片质感
- 水印添加:支持文字和图片水印,保护原创内容
- 拼图创作:多图组合创意拼接
- 绘画涂鸦:自由涂鸦标记
1.3 工程初始化
使用DevEco Studio创建标准的Stage模型工程,并集成ZRouter路由管理方案:
# 安装ZRouter
ohpm install @hzw/zrouter
工程基础架构采用Tab组件搭建多页面结构,实现功能模块的清晰划分。

二、图片压缩功能实战
图片压缩是图片处理应用的基础功能,主要涉及四个核心步骤:选择图片 → 压缩处理 → 保存到图库 → 分享图片。

2.1 选择图片
HarmonyOS提供了PhotoViewPicker选择器,可便捷地从相册选择或拍照获取图片:

import { photoAccessHelper } from '@kit.MediaLibraryKit';
async selectPhoto() {
// 创建照片选择参数对象
const photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
// 仅选择图片类型
photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
// 限制最多选择1张
photoSelectOptions.maxSelectNumber = 1;
// 创建系统相册选择器实例
const photoPicker = new photoAccessHelper.PhotoViewPicker();
// 打开选择器并获取用户选择结果
const photoSelectResult = await photoPicker.select(photoSelectOptions);
// 保存图片URI供后续使用
this.selectedPhotoUri = photoSelectResult?.photoUris?.[0] ?? '';
}
2.2 图片压缩核心逻辑
使用ImagePacker进行图片压缩编码,支持自定义压缩质量:

import { image } from '@kit.ImageKit';
import { fileIo } from '@kit.CoreFileKit';
async compressImage() {
if (!this.selectedPhotoUri) {
AlertDialog.show({ message: '请先选择图片' });
return;
}
try {
const context = this.getUIContext().getHostContext()!;
// 以只读方式打开所选图片
const file = fileIo.openSync(this.selectedPhotoUri, fileIo.OpenMode.READ_ONLY);
// 基于文件描述符创建ImageSource
const src = image.createImageSource(file.fd);
// 创建ImagePacker实例
const packer = image.createImagePacker();
// 构造压缩目标路径
const targetPath = context.cacheDir + '/' + Date.now() + '.jpg';
const newFile = fileIo.openSync(targetPath,
fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE);
// 压缩图片为JPEG格式
await packer.packToFile(src, newFile.fd, {
format: 'image/jpeg',
quality: 10 // 质量参数:0-100,数值越小压缩越强
});
this.compressedFilePath = targetPath;
AlertDialog.show({ message: '压缩成功' });
} catch (err) {
AlertDialog.show({ message: `压缩失败:${JSON.stringify(err)}` });
}
}
2.3 保存到图库
使用SaveButton安全控件和photoAccessHelper将压缩后的图片保存到系统图库:
import { fileUri } from '@kit.CoreFileKit';
async saveToGallery() {
if (!this.compressedFilePath) {
AlertDialog.show({ message: '请先执行压缩操作' });
return;
}
try {
const context = this.getUIContext().getHostContext()!;
const uri = fileUri.getUriFromPath(this.compressedFilePath);
// 创建图片资产改变请求
const assetChangeRequest =
photoAccessHelper.MediaAssetChangeRequest.createImageAssetRequest(context, uri);
// 获取相册管理实例
const phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
// 应用图片资产改变请求
await phAccessHelper.applyChanges(assetChangeRequest);
AlertDialog.show({ message: '已保存到图库' });
} catch (err) {
AlertDialog.show({ message: `保存失败:${JSON.stringify(err)}` });
}
}
关键点说明:
SaveButton是安全控件,首次使用需用户授权,后续操作自动获得一分钟访问权限- 无需申请复杂的媒体库权限即可实现保存功能
2.4 分享图片
集成ShareKit实现跨应用分享:

import { systemShare } from '@kit.ShareKit';
import { uniformTypeDescriptor } from '@kit.ArkData';
import { common } from '@kit.AbilityKit';
async shareImage() {
try {
const uiContext: UIContext = this.getUIContext();
const context: common.UIAbilityContext =
uiContext.getHostContext() as common.UIAbilityContext;
// 根据文件扩展名确定UTD类型
let utdTypeId: string;
const ext = this.selectedPhotoUri.split('.').pop();
switch (ext) {
case 'jpeg':
case 'jpg':
utdTypeId = uniformTypeDescriptor.getUniformDataTypeByFilenameExtension(
'.jpg', uniformTypeDescriptor.UniformDataType.IMAGE);
break;
case 'png':
utdTypeId = uniformTypeDescriptor.getUniformDataTypeByFilenameExtension(
'.png', uniformTypeDescriptor.UniformDataType.IMAGE);
break;
default:
utdTypeId = uniformTypeDescriptor.getUniformDataTypeByFilenameExtension(
'.jpg', uniformTypeDescriptor.UniformDataType.IMAGE);
}
// 创建分享数据
const shareData: systemShare.SharedData = new systemShare.SharedData({
utd: utdTypeId,
title: '图片分享',
description: '来自可可图片编辑',
uri: fileUri.getUriFromPath(this.selectedPhotoUri),
});
// 创建分享控制器
const controller: systemShare.ShareController =
new systemShare.ShareController(shareData);
// 显示分享面板
await controller.show(context, {
selectionMode: systemShare.SelectionMode.SINGLE,
previewMode: systemShare.SharePreviewMode.DETAIL
});
console.log('分享成功');
} catch (e) {
console.error('分享失败', e);
}
}
三、图片裁剪功能实现
图片裁剪的核心是利用Canvas画布技术实现自定义区域选择和图像绘制。

3.1 Canvas基础知识
Canvas组件提供了强大的2D绘图能力,使用步骤如下:
@Component
struct CanvasDemo {
// 1. 配置抗锯齿参数
private settings: RenderingContextSettings = new RenderingContextSettings(true);
// 2. 创建画布上下文
private context: CanvasRenderingContext2D =
new CanvasRenderingContext2D(this.settings);
build() {
Column() {
// 3. 渲染画布组件
Canvas(this.context)
.width('100%')
.height('100%')
.onReady(() => {
// 4. 在画布上绘制图形
this.context.strokeRect(50, 50, 200, 150);
})
}
}
}
Canvas坐标系:以左上角为原点(0,0),向右为X轴正方向,向下为Y轴正方向。

3.2 Canvas常用绘图API
绘制直线
this.context.moveTo(10, 10); // 起点
this.context.lineTo(100, 100); // 终点
this.context.stroke(); // 描边
绘制矩形
// 方式1:使用直线组合
this.context.moveTo(10, 10);
this.context.lineTo(300, 10);
this.context.lineTo(300, 300);
this.context.lineTo(10, 300);
this.context.closePath(); // 闭合路径
this.context.stroke();
// 方式2:直接绘制矩形
this.context.strokeRect(50, 50, 200, 150);
绘制弧形
// arc(x, y, radius, startAngle, endAngle, counterclockwise)
// 角度单位为弧度:360° = 2π ≈ 6.28
this.context.beginPath();
this.context.arc(100, 75, 50, 0, Math.PI / 2);
this.context.stroke();
绘制文本
this.context.font = '55px sans-serif';
this.context.fillText("Hello World!", 20, 60); // 填充文本
this.context.strokeText("Hello World!", 20, 120); // 描边文本
3.3 图片裁剪核心实现
裁剪功能结合ImageSource和drawImage方法实现:
import { image } from '@kit.ImageKit';
async cropImage() {
if (!this.selectedPhotoUri) {
AlertDialog.show({ message: '请先选择图片' });
return;
}
try {
// 打开图片文件
const file = fileIo.openSync(this.selectedPhotoUri, fileIo.OpenMode.READ_ONLY);
const src = image.createImageSource(file.fd);
// 获取图片信息
const info: image.ImageInfo = await src.getImageInfo();
const w: number = info.size.width;
const h: number = info.size.height;
// 创建PixelMap
const pm: image.PixelMap = await src.createPixelMap();
// 定义裁剪区域参数
const cropSize: number = 100;
const sx: number = 200; // 裁剪起始X坐标
const sy: number = 200; // 裁剪起始Y坐标
const sw: number = Math.min(cropSize, w); // 裁剪宽度
const sh: number = Math.min(cropSize, h); // 裁剪高度
// 清空画布并绘制裁剪区域
this.context.clearRect(0, 0, 200, 200);
// drawImage(源图片, 源X, 源Y, 源宽, 源高, 目标X, 目标Y, 目标宽, 目标高)
this.context.drawImage(pm, sx, sy, sw, sh, 0, 0, sw, sh);
console.log(`裁剪成功: ${sx},${sy},${sw}x${sh}`);
} catch (err) {
AlertDialog.show({ message: `裁剪失败:${JSON.stringify(err)}` });
}
}
技术要点:
createPixelMap创建可操作的像素图对象drawImage支持9参数模式实现精准裁剪- 通过调整源区域(sx, sy, sw, sh)实现任意位置裁剪
四、滤镜效果开发
滤镜功能基于Image组件的colorFilter属性实现,通过颜色矩阵变换改变图片色调。


4.1 colorFilter核心原理
colorFilter使用4×5颜色矩阵对每个像素的RGBA值进行数学变换:
R' = r1*R + r2*G + r3*B + r4*A + r5*255
G' = g1*R + g2*G + g3*B + g4*A + g5*255
B' = b1*R + b2*G + b3*B + b4*A + b5*255
A' = a1*R + a2*G + a3*B + a4*A + a5*255
矩阵格式:
[r1, r2, r3, r4, r5,
g1, g2, g3, g4, g5,
b1, b2, b3, b4, b5,
a1, a2, a3, a4, a5]

4.2 常用滤镜矩阵
原图效果(单位矩阵)

const originalMatrix = [
1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0
];
灰度滤镜

const grayMatrix = [
0.299, 0.587, 0.114, 0, 0,
0.299, 0.587, 0.114, 0, 0,
0.299, 0.587, 0.114, 0, 0,
0, 0, 0, 1, 0
];
const grayFilter: ColorFilter = new ColorFilter(grayMatrix);
颜色反转(负片效果)

const invertMatrix = [
-1, 0, 0, 0, 1,
0,-1, 0, 0, 1,
0, 0,-1, 0, 1,
0, 0, 0, 1, 0
];
棕褐色怀旧效果

const sepiaMatrix = [
0.393, 0.769, 0.189, 0, 0,
0.349, 0.686, 0.168, 0, 0,
0.272, 0.534, 0.131, 0, 0,
0, 0, 0, 1, 0
];
亮度调节

const brightness = 0.3; // 正数变亮,负数变暗
const brightnessMatrix = [
1, 0, 0, 0, brightness,
0, 1, 0, 0, brightness,
0, 0, 1, 0, brightness,
0, 0, 0, 1, 0
];
暖色调/冷色调

暖色调
const warmMatrix = [
1.2, 0, 0, 0, 0, // 增强红色
0, 1.1, 0, 0, 0, // 增强绿色
0, 0, 0.9, 0, 0, // 减弱蓝色
0, 0, 0, 1, 0
];
冷色调
const coolMatrix = [
0.9, 0, 0, 0, 0, // 减弱红色
0, 1.0, 0, 0, 0,
0, 0, 1.2, 0, 0, // 增强蓝色
0, 0, 0, 1, 0
];
4.3 使用示例
@Component
struct FilterDemo {
private grayMatrix: number[] = [
0.299, 0.587, 0.114, 0, 0,
0.299, 0.587, 0.114, 0, 0,
0.299, 0.587, 0.114, 0, 0,
0, 0, 0, 1, 0
];
private grayFilter: ColorFilter = new ColorFilter(this.grayMatrix);
build() {
Column({ space: 20 }) {
// 原图
Image($r('app.media.startIcon'))
.width(100)
.height(100)
// 应用灰度滤镜
Image($r('app.media.startIcon'))
.width(100)
.height(100)
.colorFilter(this.grayFilter)
}
}
}
4.4 使用DrawingColorFilter(推荐)
HarmonyOS还提供了更便捷的DrawingColorFilter API:

import { drawing } from '@kit.ArkGraphics2D';
// 方式1:使用混合模式着色
let redFilter = drawing.ColorFilter.createBlendModeColorFilter(
{ alpha: 255, red: 255, green: 0, blue: 0 },
drawing.BlendMode.SRC_IN
);
// 方式2:使用矩阵
let matrix = [
0.299, 0.587, 0.114, 0, 0,
0.299, 0.587, 0.114, 0, 0,
0.299, 0.587, 0.114, 0, 0,
0, 0, 0, 1, 0
];
let grayFilter = drawing.ColorFilter.createMatrixColorFilter(matrix);
// 应用滤镜
Image($r('app.media.startIcon'))
.colorFilter(redFilter);
五、水印功能实现
水印功能巧妙结合了UI层叠布局和组件截图技术。

5.1 实现思路
- 预览阶段:使用Stack层叠布局将水印文本/图片覆盖在原图上
- 保存阶段:使用
componentSnapshot对整个组件截图生成带水印的新图片
5.2 组件截图API详解
componentSnapshot是HarmonyOS提供的组件截图能力,可将UI组件转换为PixelMap图片数据。
基本使用步骤
1. 为组件添加唯一标识
Stack() {
// 组件内容
}
.id("targetComponent")
2. 获取截图(异步方式)
async takeScreenshot() {
try {
const uiContext = this.getUIContext();
const pixelMap = await uiContext.getComponentSnapshot()
.get('targetComponent', {
scale: 1.0, // 缩放比例
waitUntilRenderFinished: true // 等待渲染完成
});
// 使用pixelMap进行后续处理
this.screenshotImage = pixelMap;
} catch (error) {
console.error('截图失败:', error);
}
}
3. 获取截图(同步方式)

takeScreenshot() {
try {
const uiContext = this.getUIContext();
const pixelMap = uiContext.getComponentSnapshot()
.getSync('targetComponent', {
scale: 1.0,
waitUntilRenderFinished: true
});
this.screenshotImage = pixelMap;
} catch (error) {
console.error('截图失败:', error);
}
}
5.3 SnapshotOptions配置参数
| 参数 | 类型 | 说明 |
|---|---|---|
| scale | number | 缩放比例,范围0.1-1.0,默认1.0 |
| waitUntilRenderFinished | boolean | 是否等待渲染完成(推荐true) |
| region | Object | 指定截图区域 |
| region.start | number | 起始X坐标 |
| region.top | number | 起始Y坐标 |
| region.end | number | 结束X坐标 |
| region.bottom | number | 结束Y坐标 |
5.4 完整水印实现示例
import { image } from '@kit.ImageKit';
@ComponentV2
struct WatermarkDemo {
@Local
screenshotImage: image.PixelMap | undefined = undefined;
// 添加水印并截图
addWatermarkAndCapture = async () => {
try {
const uiContext = this.getUIContext();
// 截取带水印的组件
const pixelMap = await uiContext.getComponentSnapshot()
.get('watermarkContainer', {
scale: 1.0,
waitUntilRenderFinished: true
});
this.screenshotImage = pixelMap;
// 可选:保存到图库
// await this.saveToGallery(pixelMap);
} catch (error) {
console.error('水印添加失败:', error);
}
}
build() {
Column({ space: 20 }) {
Button("添加水印")
.onClick(this.addWatermarkAndCapture)
// 原图+水印层叠布局
Stack({ alignContent: Alignment.BottomEnd }) {
// 原始图片
Image($r("app.media.startIcon"))
.width(200)
.height(200)
// 水印文字
Text("© 可可图片编辑")
.fontColor(Color.White)
.backgroundColor(Color.Black)
.opacity(0.6)
.padding(8)
}
.width(200)
.height(200)
.id("watermarkContainer") // 重要:设置ID用于截图
// 显示截图结果
if (this.screenshotImage) {
Image(this.screenshotImage)
.width(100)
.height(100)
}
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
关键技术点:
- 使用Stack实现水印覆盖布局
- 通过
alignContent调整水印位置(TopStart/TopEnd/BottomStart/BottomEnd等) - 使用
opacity调整水印透明度 waitUntilRenderFinished: true确保水印渲染完成后再截图
六、应用打包与发布
6.1 在AGC创建应用
访问AppGallery Connect控制台创建HarmonyOS应用:
- 官方文档:https://developer.huawei.com/consumer/cn/doc/app/agc-help-createharmonyapp-0000001945392297
6.2 配置应用签名证书
在DevEco Studio中配置签名信息:
- 生成密钥库文件(.p12)
- 申请证书(.cer)
- 申请Profile文件(.p7b)
- 在build-profile.json5中配置签名信息
详细步骤参考:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ide-publish-app
6.3 构建发布包
# 构建Release版本APP包
hvigorw assembleApp --mode module -p product=default -p buildMode=release
6.4 发布到应用市场
通过AGC控制台上传APP包并完成应用发布:
- 填写应用信息(名称、图标、截图、描述等)
- 设置应用分类和标签
- 提交审核
- 审核通过后发布上架
发布指南:https://developer.huawei.com/consumer/cn/doc/app/agc-help-release-app-guide-0000002287176372
七、技术总结与最佳实践
7.1 核心技术点回顾
| 功能模块 | 核心API | 关键技术 |
|---|---|---|
| 图片选择 | PhotoViewPicker | MediaLibraryKit |
| 图片压缩 | ImagePacker | 质量参数控制 |
| 保存图库 | SaveButton + photoAccessHelper | 安全控件授权 |
| 图片分享 | ShareKit | UTD类型识别 |
| 图片裁剪 | Canvas + drawImage | 坐标系计算 |
| 滤镜效果 | colorFilter | 颜色矩阵变换 |
| 水印添加 | componentSnapshot | 组件截图 |
7.2 开发建议
- 权限管理:优先使用安全控件(SaveButton等)减少权限申请
- 性能优化:大图压缩前先进行尺寸缩放,避免内存溢出
- 用户体验:异步操作添加Loading提示,避免界面卡顿
- 错误处理:完善try-catch机制,提供友好的错误提示
- 代码复用:将通用图片处理逻辑封装为工具类
7.3 扩展方向
- 支持批量图片处理
- 添加更多滤镜效果(马赛克、模糊、锐化等)
- 实现图片拼接与拼图功能
- 集成AI能力(智能抠图、图像修复等)
- 支持GIF动图编辑
结语
通过本文的实战教程,我们完整实现了一款功能丰富的HarmonyOS图片处理应用。项目涵盖了图片压缩、裁剪、滤镜、水印等核心功能,深入使用了HarmonyOS的媒体库、Canvas绘图、组件截图等关键技术。
希望本文能帮助开发者快速掌握HarmonyOS图像处理的开发技巧,为构建更多优秀的原生应用打下坚实基础。完整源码和更多技术细节,欢迎体验应用并交流讨论!
作者简介:HarmonyOS开发实践者,专注于移动应用开发与技术分享。
本文示例代码基于:HarmonyOS 5.0.2(14) SDK
参考文档:
- HarmonyOS官方开发文档
- ArkUI组件参考
- 媒体库开发指南
更多推荐


所有评论(0)