当前位置: 首页 > news >正文

HarmonyOS APP<<古今职鉴定>>开源教程第22篇:图片处理与资源管理

本篇学习图片处理能力和资源管理,实现祝福卡片图片处理

图:图片处理与资源管理 的关键流程与实现要点。

学习目标

完成本篇后,你将能够:

  • ✅ 掌握图片资源类型
  • ✅ 配置图片组件属性
  • ✅ 使用图片处理能力
  • ✅ 管理应用资源文件

预计学习时间

约 90 分钟


实战一:图片资源类型

第一步:本地图片资源

// 使用 $r 引用 media 目录下的图片 Image($r('app.media.icon')) .width(100) .height(100) // 使用 $rawfile 引用 rawfile 目录下的图片 Image($rawfile('images/photo.png')) .width(100) .height(100)

第二步:网络图片

Image('https://example.com/image.png') .width(100) .height(100)

第三步:Base64 图片

const base64Image = 'data:image/png;base64,iVBORw0KGgo...'; Image(base64Image) .width(100) .height(100)

第四步:PixelMap 图片

import { image } from '@kit.ImageKit'; @State pixelMap: image.PixelMap | null = null; // 使用 PixelMap if (this.pixelMap) { Image(this.pixelMap) .width(100) .height(100) }

实战二:图片组件配置

第一步:填充模式

Image($r('app.media.photo')) .width(200) .height(150) .objectFit(ImageFit.Cover) // 填充模式
模式说明
Contain保持比例,完整显示
Cover保持比例,填满容器
Fill拉伸填满,可能变形
None原始尺寸
ScaleDown缩小或原始尺寸

案例效果

┌─────────────────────────────────────────┐ │ 填充模式对比 │ │ │ │ ┌──────┐ ┌──────┐ ┌──────┐ │ │ │ 福 │ │██████│ │██████│ │ │ │ │ │██福██│ │█福███│ │ │ │ 完整 │ │██████│ │██████│ │ │ └──────┘ └──────┘ └──────┘ │ │ Contain Cover Fill │ │ 保持比例 填满裁切 拉伸填满 │ └─────────────────────────────────────────┘

第二步:图片插值

Image($r('app.media.photo')) .interpolation(ImageInterpolation.High) // 高质量插值

第三步:渲染模式

// 原始模式 Image($r('app.media.icon')) .renderMode(ImageRenderMode.Original) // 模板模式(可着色) Image($r('app.media.icon')) .renderMode(ImageRenderMode.Template) .fillColor('#c41e3a')

第四步:图片圆角和边框

Image($r('app.media.avatar')) .width(80) .height(80) .borderRadius(40) // 圆形 .border({ width: 2, color: '#c41e3a' })

实战三:图片处理能力

第一步:创建 PixelMap

import { image } from '@kit.ImageKit'; async function createPixelMap(width: number, height: number): Promise<image.PixelMap> { const options: image.InitializationOptions = { size: { width, height }, pixelFormat: image.PixelMapFormat.RGBA_8888, alphaType: image.AlphaType.IMAGE_ALPHA_TYPE_OPAQUE }; return await image.createPixelMap(options); }

第二步:从资源创建 PixelMap

async function loadImageFromResource(resourceName: string): Promise<image.PixelMap | null> { try { const context = getContext(this); const resourceManager = context.resourceManager; // 读取资源 const imageData = await resourceManager.getMediaContent($r(`app.media.${resourceName}`)); // 创建 ImageSource const imageSource = image.createImageSource(imageData.buffer); // 解码为 PixelMap const pixelMap = await imageSource.createPixelMap(); return pixelMap; } catch (error) { console.error('加载图片失败:', error); return null; } }

第三步:图片裁剪

async function cropImage( pixelMap: image.PixelMap, x: number, y: number, width: number, height: number ): Promise<void> { await pixelMap.crop({ x, y, size: { width, height } }); }

第四步:图片缩放

async function scaleImage( pixelMap: image.PixelMap, scaleX: number, scaleY: number ): Promise<void> { await pixelMap.scale(scaleX, scaleY); }

实战四:资源管理器

第一步:获取资源管理器

import { resourceManager } from '@kit.LocalizationKit'; const context = getContext(this); const resManager = context.resourceManager;

第二步:读取 rawfile 资源

async function readRawFile(fileName: string): Promise<Uint8Array> { const context = getContext(this); const resManager = context.resourceManager; const data = await resManager.getRawFileContent(fileName); return data; }

第三步:读取媒体资源

async function readMediaResource(resourceId: Resource): Promise<Uint8Array> { const context = getContext(this); const resManager = context.resourceManager; const data = await resManager.getMediaContent(resourceId); return data; }

第四步:写入临时文件

import { fileIo } from '@kit.CoreFileKit'; async function writeToTempFile(data: Uint8Array, fileName: string): Promise<string> { const context = getContext(this); const tempDir = context.tempDir; const filePath = `${tempDir}/${fileName}`; const file = fileIo.openSync(filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY); fileIo.writeSync(file.fd, data.buffer); fileIo.closeSync(file.fd); return filePath; }

实战五:祝福卡片图片处理

第一步:创建图片处理页面

import { image } from '@kit.ImageKit'; import { fileIo } from '@kit.CoreFileKit'; interface BlessingCard { name: string; title: string; resource: Resource; } @Entry @Component struct Lesson22Page { @State currentCard: BlessingCard | null = null; @State cardPixelMap: image.PixelMap | null = null; @State isLoading: boolean = false; private blessingCards: BlessingCard[] = [ { name: 'fortune', title: '福', resource: $r('app.media.blessing_fortune') }, { name: 'wealth', title: '财', resource: $r('app.media.blessing_gold_ingot') }, { name: 'happiness', title: '喜', resource: $r('app.media.blessing_happy_newyear') }, { name: 'longevity', title: '寿', resource: $r('app.media.blessing_good_luck') }, { name: 'prosperity', title: '禄', resource: $r('app.media.blessing_five_fortune') } ]; aboutToAppear() { this.selectRandomCard(); } selectRandomCard() { const index = Math.floor(Math.random() * this.blessingCards.length); this.currentCard = this.blessingCards[index]; } async loadCardImage() { if (!this.currentCard) return; this.isLoading = true; try { const context = getContext(this); const resManager = context.resourceManager; // 读取图片资源 const imageData = await resManager.getMediaContent(this.currentCard.resource); // 创建 ImageSource const imageSource = image.createImageSource(imageData.buffer); // 解码为 PixelMap this.cardPixelMap = await imageSource.createPixelMap(); } catch (error) { console.error('加载图片失败:', error); } finally { this.isLoading = false; } } async saveToTempFile(): Promise<string | null> { if (!this.currentCard) return null; try { const context = getContext(this); const resManager = context.resourceManager; // 读取图片资源 const imageData = await resManager.getMediaContent(this.currentCard.resource); // 写入临时文件 const tempDir = context.tempDir; const filePath = `${tempDir}/${this.currentCard.name}.png`; const file = fileIo.openSync(filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY); fileIo.writeSync(file.fd, imageData.buffer); fileIo.closeSync(file.fd); return filePath; } catch (error) { console.error('保存文件失败:', error); return null; } } build() { Column() { // 头部 Row() { Text('图片处理') .fontSize(18) .fontWeight(FontWeight.Bold) .fontColor('#1e293b') } .width('100%') .height(56) .padding({ left: 16, right: 16 }) .backgroundColor(Color.White) Scroll() { Column({ space: 20 }) { // 当前卡片 Column({ space: 12 }) { Text('当前祝福卡片') .fontSize(16) .fontWeight(FontWeight.Medium) .fontColor('#1e293b') if (this.currentCard) { Column({ space: 8 }) { Image(this.currentCard.resource) .width(200) .height(200) .objectFit(ImageFit.Contain) .borderRadius(12) Text(this.currentCard.title) .fontSize(24) .fontWeight(FontWeight.Bold) .fontColor('#c41e3a') } } Button('换一张') .onClick(() => { this.selectRandomCard(); }) .backgroundColor('#c41e3a') } .width('100%') .padding(16) .backgroundColor(Color.White) .borderRadius(12) .alignItems(HorizontalAlign.Center) // 图片操作 Column({ space: 12 }) { Text('图片操作') .fontSize(16) .fontWeight(FontWeight.Medium) .fontColor('#1e293b') Row({ space: 12 }) { Button('加载 PixelMap') .layoutWeight(1) .onClick(async () => { await this.loadCardImage(); }) Button('保存到临时目录') .layoutWeight(1) .onClick(async () => { const path = await this.saveToTempFile(); if (path) { AlertDialog.show({ title: '保存成功', message: `文件路径: ${path}`, primaryButton: { value: '确定', action: () => {} } }); } }) } .width('100%') if (this.isLoading) { LoadingProgress() .width(32) .height(32) } if (this.cardPixelMap) { Text('PixelMap 已加载') .fontSize(14) .fontColor('#22c55e') } } .width('100%') .padding(16) .backgroundColor(Color.White) .borderRadius(12) .alignItems(HorizontalAlign.Start) // 图片展示模式 Column({ space: 12 }) { Text('填充模式对比') .fontSize(16) .fontWeight(FontWeight.Medium) .fontColor('#1e293b') Row({ space: 8 }) { Column({ space: 4 }) { Image($r('app.media.blessing_fortune')) .width(80) .height(60) .objectFit(ImageFit.Contain) .backgroundColor('#f0f0f0') .borderRadius(4) Text('Contain') .fontSize(10) .fontColor('#64748b') } Column({ space: 4 }) { Image($r('app.media.blessing_fortune')) .width(80) .height(60) .objectFit(ImageFit.Cover) .backgroundColor('#f0f0f0') .borderRadius(4) Text('Cover') .fontSize(10) .fontColor('#64748b') } Column({ space: 4 }) { Image($r('app.media.blessing_fortune')) .width(80) .height(60) .objectFit(ImageFit.Fill) .backgroundColor('#f0f0f0') .borderRadius(4) Text('Fill') .fontSize(10) .fontColor('#64748b') } } } .width('100%') .padding(16) .backgroundColor(Color.White) .borderRadius(12) .alignItems(HorizontalAlign.Start) } .padding(16) } .layoutWeight(1) } .width('100%') .height('100%') .backgroundColor('#f8f6f5') } } @Builder export function Lesson22PageBuilder() { Lesson22Page() }

案例效果:页面运行后展示如下界面:

┌─────────────────────────────────────┐ │ 图片处理 │ ├─────────────────────────────────────┤ │ │ │ ┌─────────────────────────────┐ │ │ │ 当前祝福卡片 │ │ │ │ │ │ │ │ ┌──────────┐ │ │ │ │ │ │ │ │ │ │ │ 🧧福 │ │ │ │ │ │ │ │ │ │ │ └──────────┘ │ │ │ │ 福 │ │ │ │ │ │ │ │ [ 换一张 ] │ │ │ └─────────────────────────────┘ │ │ │ │ ┌─────────────────────────────┐ │ │ │ 图片操作 │ │ │ │ │ │ │ │ [加载PixelMap] [保存到临时] │ │ │ │ │ │ │ │ ✅ PixelMap 已加载 │ │ │ └─────────────────────────────┘ │ │ │ │ ┌─────────────────────────────┐ │ │ │ 填充模式对比 │ │ │ │ │ │ │ │ ┌────┐ ┌────┐ ┌────┐ │ │ │ │ │ 福 │ │█福█│ │█福█│ │ │ │ │ └────┘ └────┘ └────┘ │ │ │ │ Contain Cover Fill │ │ │ └─────────────────────────────┘ │ └─────────────────────────────────────┘

效果说明: - 顶部展示随机抽取的祝福卡片图片和标题 - 点击「换一张」随机切换不同祝福卡片 - 「加载 PixelMap」按钮加载后显示绿色 ✅ 提示 - 「保存到临时目录」成功后弹出路径弹窗 - 底部展示 Contain/Cover/Fill 三种填充模式的对比效果

第二步:运行验证

hvigorw assembleHap --no-daemon

本课小结

核心知识点

知识点说明
$r()引用 media 资源
$rawfile()引用 rawfile 资源
objectFit图片填充模式
PixelMap图片像素数据
resourceManager资源管理器

图片处理流程

  1. 获取资源管理器
  2. 读取图片数据
  3. 创建 ImageSource
  4. 解码为 PixelMap
  5. 进行图片处理
  6. 保存或显示

课后练习

练习1:实现图片滤镜

为祝福卡片添加灰度、模糊等滤镜效果。

练习2:实现图片合成

将祝福文字合成到卡片图片上。


下一课预告

第23课我们将进入案例实战篇,完整开发职官词典模块,包括:

  • 需求分析与设计
  • 数据层实现
  • 列表页与详情页
  • 收藏功能

项目开源地址

https://gitcode.com/daleishen/gujinzhijian

http://www.jsqmd.com/news/865525/

相关文章:

  • 2026西宁婚纱摄影推荐TOP5!这几家口碑好到爆! - charlieruizvin
  • 2026年北京被动房全案服务商选型指南|利坚美EPC总承包如何破局权责推诿陷阱 - 企业名录优选推荐
  • 2026全球名义雇主EOR服务商优选,泰国海外人力资源服务商推荐 - 品牌2025
  • 国内核心空港空运报关服务商技术能力实测对比 - 奔跑123
  • 过度设计是程序员的“职业病”,如何克制?
  • 推挽变压器深度解析:隔离电源设计中的选型准则与工程验证
  • 拍了一堆没修图的照片发不出去?这个私有相册让我终于不用再“表演”生活了
  • 从化区做美妆日化老板娘自己管账多年账务很乱换哪家代账公司能规范?|3个风险点+梳理路径全解 - 欢欢在创业
  • 简单比复杂更难:技术人如何修炼“简化”的能力?
  • 【信息科学与工程学】计算机科学与自动化——第十五篇 云计算 第三系列 亿级并发的算法
  • 成都搬家公司哪家靠谱?2026 口碑 TOP5 新鲜出炉 - 资讯速览
  • 长沙学校毕业典礼大型活动拍摄:定格现场温度 留存记忆 - 奔跑123
  • Forza Painter完整指南:如何将任何图片转换为《极限竞速》专业车辆涂装
  • 如何用3步将B站缓存视频变回可播放的MP4文件?
  • GitHub Copilot多模型集成深度解析:开发者如何根据场景选择最优AI助手?
  • 出海业务网络怎么选:专线还是 SD-WAN?
  • 商用智能咖啡机采购指南:智能咖啡机怎么选?多维度对比哪家技术强 - 品牌2025
  • 使用 Codex 的一次危险提醒
  • 亨得利夜间停走故障维修深度解析:2026年7城实探,从“睡一觉就不走了”到满血复活全记录 - 亨得利腕表维修中心
  • UWB汽车雷达哨兵模式全解 | 全网独家拆解,功耗+隐私双突破篇 引入低功耗唤醒机制+隐私脱敏算法,破解传统哨兵续航短、隐私泄露痛点,助力车载安防量产落地
  • 2026年北京被动房全案服务商选型指南:从设计咨询到PHI认证的完整对标 - 企业名录优选推荐
  • 2026年北京被动房全案服务商选型指南|从设计到认证的零碳建筑一站式方案 - 企业名录优选推荐
  • 问了4个AI模型,推荐门店稽查公司竟没有一家提到小零科技?问题出在哪
  • 2026 上海卡地亚戒指回收避坑攻略:五大平台专业度与套路揭秘 - 李宏哲1
  • 2026年昆明财税行业发展现状 代理记账服务多维信息梳理 - 兔兔不是荼荼
  • Neo4j 知识图谱:实体建模+Cypher查询+LangChain接入
  • Reliance Electric 805401-5R电源模块接口架
  • 2026年北京被动房与超低能耗建筑全产业链服务商选型指南 - 企业名录优选推荐
  • 证件照怎样换背景?5大背景更换软件对比,微信小程序快速出片方案
  • 2026进口箱式电阻炉选哪个品牌?按需匹配容积温度精准选型 - 品牌推荐大师