鸿蒙开发案例:【图像加载缓存库ImageKnife】

专门为OpenHarmony打造的一款图像加载缓存库,致力于更高效、更轻便、更简单。

简介

OpenHarmony的自研版本:

  • 支持内存缓存,使用LRUCache算法,对图片数据进行内存缓存。
  • 支持磁盘缓存,对于下载图片会保存一份至磁盘当中。
  • 支持进行图片变换: 支持图像像素源图片变换效果。
  • 支持用户配置参数使用:( 例如:配置是否开启一级内存缓存,配置磁盘缓存策略,配置仅使用缓存加载数据,配置图片变换效果,配置占位图,配置加载失败占位图等)。
  • 推荐使用ImageKnifeComponent组件配合ImageKnifeOption参数来实现功能。
  • 支持用户自定义配置实现能力参考ImageKnifeComponent组件中对于入参ImageKnifeOption的处理。

下载安装

ohpm install @ohos/imageknife

使用说明

1.依赖配置

在entry\src\main\ets\entryability\EntryAbility.ts中做如下配置初始化全局ImageKnife实例:

import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';
import { ImageKnife } from '@ohos/imageknife'

export default class EntryAbility extends UIAbility {
  onWindowStageCreate(windowStage: window.WindowStage) {
    windowStage.loadContent('pages/Index', (err, data) => {
    });
    // 初始化全局ImageKnife 
    ImageKnife.with(this.context);
  	// 后续访问ImageKnife请通过:ImageKnifeGlobal.getInstance().getImageKnife()方式
  }
}

2.加载普通图片

接下来我们来写个简单实例看看:

import { ImageKnifeComponent, ImageKnifeOption } from '@ohos/imageknife'

@Entry
@Component
struct Index {
  @State message: string = 'Hello World'
  @State option: ImageKnifeOption = {
    loadSrc: $r('app.media.icon')
  }

  build() {
      Row() {
        Column() {
          Text(this.message)
            .fontSize(50)
            .fontWeight(FontWeight.Bold)
          ImageKnifeComponent({ imageKnifeOption: this.option })
            .width(300)
            .height(300)
        }.width('100%')
      }.height('100%')
  }
}

非常简单,仅需定义一个ImageKnifeOption数据对象,然后在你需要的UI位置,加入ImageKnifeComponent自定义组件就可以加载出一张图像了。

3.加载SVG图片

加载svg其实和普通流程没有区别,只要将 loadSrc: $r('app.media.jpgSample'), 改成一张 loadSrc: $r('app.media.svgSample'), svg类型图片即可。

4.加载GIF图片

加载GIF其实和普通流程也没有区别只要将 loadSrc: $r('app.media.jpgSample'), 改成一张 loadSrc: $r('app.media.gifSample'), GIF图片即可。

5.自定义Key

因为通常改变标识符比较困难或者根本不可能,所以ImageKnife也提供了 签名 API 来混合(你可以控制的)额外数据到你的缓存键中。 签名(signature)适用于媒体内容,也适用于你可以自行维护的一些版本元数据。

将签名传入加载请求

imageKnifeOption = {
                loadSrc: 'https://aahyhy.oss-cn-beijing.aliyuncs.com/blue.jpg',
                signature: new ObjectKey(new Date().getTime().toString())
              }

详细样例请参考SignatureTestPage文件

代码示例

进阶使用

如果简单的加载一张图像无法满足需求,我们可以看看ImageKnifeOption这个类提供了哪些扩展能力。

ImageKnifeOption参数列表

参数名称入参内容功能简介
loadSrcstringPixelMap\Resource
mainScaleTypeScaleType设置主图展示样式(可选)
strategyDiskStrategy设置磁盘缓存策略(可选)
dontAnimateFlagbooleangif加载展示一帧(可选)
placeholderSrcPixelMapResource占位图数据源
placeholderScaleTypeScaleType设置占位图展示样式(可选)
errorholderSrcPixelMapResource错误占位图数据源
errorholderSrcScaleTypeScaleType设置失败占位图展示样式(可选)
retryholderSrcPixelMapResource重试占位图数据源
retryholderScaleTypeScaleType设置重试占位图展示样式(可选)
thumbSizeMultipliernumber 范围(0,1]设置缩略图占比(可选)
thumbSizeDelaynumber设置缩略图展示时间(可选)
thumbSizeMultiplierScaleTypeScaleType设置缩略图展示样式(可选)
displayProgressboolean设置是否展示下载进度条(可选)
canRetryClickboolean设置重试图层是否点击重试(可选)
onlyRetrieveFromCacheboolean仅使用缓存加载数据(可选)
isCacheableboolean是否开启一级内存缓存(可选)
gif{ // 返回一周期动画gif消耗的时间 loopFinish?: (loopTime?) => void // gif播放速率相关 speedFactory?: number // 直接展示gif第几帧数据 seekTo?: number }GIF播放控制能力(可选)
transformationBaseTransform单个变换(可选)
transformationsArray多个变换,目前仅支持单个变换(可选)
allCacheInfoCallbackIAllCacheInfoCallback输出缓存相关内容和信息(可选)
signatureObjectKey自定key(可选)
drawLifeCycleIDrawLifeCycle用户自定义实现绘制方案(可选)
imageSmoothingEnabledboolean抗锯齿是否开启属性配置,设置为false时,imageSmoothingQuality失效
imageSmoothingQualityAntiAliasing抗锯齿属性配置

其他参数只需要在ImageKnifeOption对象上按需添加即可。

这里我们着重讲一下自定义实现绘制方案。为了增强绘制扩展能力,目前ImageKnifeComponent使用了Canvas的渲染能力作为基础。在此之上为了抽象组件绘制表达。我将图像的状态使用了 IDrawLifeCycle绘制生命周期进行表达

大致流程 展示占位图->展示网络加载进度->展示缩略图->展示主图->展示重试图层->展示失败占位图

ImageKnifeComponent内部,责任链实现。 用户参数设置->全局参数设置->自定义组件内部设置

采用责任链的好处是,用户可以通过自定义绘制,重新绘制图层。如果不想绘制也可以通过预制回调获取绘制流程信息。

场景1:默认的展示不满足需求,需要加个圆角效果。

代码如下:

import { ImageKnifeComponent } from '@ohos/imageknife'
import { ImageKnifeOption } from '@ohos/imageknife'
import { ImageKnifeDrawFactory } from '@ohos/imageknife'

@Entry
@Component
struct Index {
  @State imageKnifeOption1: ImageKnifeOption = { 
      // 加载一张本地的jpg资源(必选)
      loadSrc: $r('app.media.jpgSample'),
      // 占位图使用本地资源icon_loading(可选)
      placeholderSrc: $r('app.media.icon_loading'),
      // 失败占位图使用本地资源icon_failed(可选)
      errorholderSrc: $r('app.media.icon_failed'),
      // 绘制圆角30,边框5,边框"#ff00ff".用户自定义绘制(可选)
      drawLifeCycle:ImageKnifeDrawFactory.createRoundLifeCycle(5,"#ff00ff",30)
    };
        
    build(){
        Scroll() {
          Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
            ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption1 })
            .width(300) 
            .height(300)
          }
        }
        .width('100%')
        .height('100%')
    }
}

ImageKnifeDrawFactory.createRoundLifeCycle(5,"#ff00ff",30) 我们深入查看源码可以发现,实际上是对IDrawLifeCycle接口的部分实现,这里我介绍一下IDrawLifeCycle。

IDrawLifeCycle的返回值代表事件是否被消费,如果被消费接下来组件内部就不会处理,如果没被消费就会传递到下一个使用者。目前消费流程(用户自定义-> 全局配置定义->组件内部默认定义) *

所以我们在当数据是一张PixelMap的时候(目前jpg png bmp webp svg返回的都是PixelMap,gif返回GIFFrame数组),我们返回了true。消费了事件,代表这个绘制流程用户自定义完成。

由于IDrawLifeCycle实现较为冗长,我们封装了ImageKnifeDrawFactory工厂,提供了网络下载百分比效果、圆角、椭圆添加边框等能力。下面我们就再看看使用工厂封装之后的场景代码。

场景2: 网络下载百分比效果展示

当进行加载网络图片时,可能需要展示网络下载百分比动画。但是默认的动画又不能满足需求,这个时候我们就需要自定义网络下载百分比效果。代码如下:

import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';
import { ImageKnifeGlobal,ImageKnife,ImageKnifeDrawFactory,LogUtil } from '@ohos/imageknife'
import abilityAccessCtrl,{Permissions} from '@ohos.abilityAccessCtrl';
export default class EntryAbility extends UIAbility {
    onWindowStageCreate(windowStage: window.WindowStage) {
        //.. 删除不必要代码
        windowStage.loadContent('pages/index', (err, data) => {
        });
       	// 初始化ImageKnifeGlobal和ImageKnife
        ImageKnife.with(this.context);
        // 全局配置网络加载进度条 使用ImageKnifeGlobal.getInstance().getImageKnife()访问ImageKnife
ImageKnifeGlobal.getInstance().getImageKnife().setDefaultLifeCycle(ImageKnifeDrawFactory.createProgressLifeCycle("#10a5ff", 0.5))
    }
}

这里大家可能会问,为什么会将这个IDrawLifeCycle放在AbilityStage里面实现?

这是因为网络下载百分比进度很多时候都是全局通用,如果有需要全局配置的自定义展示方案。推荐在AbilityStage里面,往ImageKnife的setDefaultLifeCycle函数中注入,即可将ImageKnifeComponent中的默认绘制方案替换。

在这里我们实现的效果如下图所示。

高级用法

以上简单使用和进阶使用都是经过一层自定义组件封装之后形成的,RequestOption封装成了ImageKnifeOption,绘制部分封装成了自定义组件ImageKnifeComponent。

如果用户其实并不关心绘制部分,或者说想用自己的通用方案对自定义组件ImageKnifeComponent重构都是可以的。

下面我们会着重指导用户如何复用图片加载逻辑,重构自定义组件ImageKnifeComponent。

首先我们先看看RequestOption构建的内容,如下所示:

数据加载

RequestOption构建:

请查阅下文接口内容:[RequestOption接口方法]

了解了RequestOption的参数内容后,我们可以参考ImageKnifeComponent组件代码进行分析。

imageKnifeExecute()函数入口,首先我们需要构建一个RequestOption对象,let request = new RequestOption(), 接下来就是按需配置request对象的内容,最后使用 ImageKnifeGlobal.getInstance().getImageKnife()?.call(request)发送request执行任务即可。

是不是很简单,而其实最重要的内容是就是: 按需配置request对象的内容 为了更好理解,我举例说明一下:

场景一: 简单加载一张图片
let request = new RequestOption();
// (必传)
request.load("图片url")
  // (可选 整个request监听回调)
	.addListener({callback:(err:BusinessError|string, data:ImageKnifeData) => {
	// data 是ImageKnifeData对象
	if(data.isPixelMap()){
	// 这样就获取到了目标PixelMap
	let pixelmap = data.drawPixleMap.imagePixelMap;
	}
    return false;
  })
 
  let compSize:Size = {
    width: this.currentWidth,
    height:this.currentHeight
  }
  // (必传)这里setImageViewSize函数必传组件大小,因为涉及到图片变换效果都需要适配图像源和组件大小
 request.setImageViewSize(compSize)
 // 最后使用ImageKnife的call函数调用request即可
 let imageKnife:ImageKnife|undefined = ImageKnifeGlobal.getInstance().getImageKnife();
 if(imageKnife != undefined){
 	imageKnife.call(request)
 }

其他场景,可以按需加载

比如我需要配置 占位图 只需要 在request对象创建好之后,调用 placeholder 函数即可

request.placeholder(this.imageKnifeOption.placeholderSrc, (data) => {
  console.log('request.placeholder callback')
  this.displayPlaceholder(data)
})

再比如 我对缓存配置有要求,我要禁用内存缓存,调用 skipMemoryCache 函数即可

request.skipMemoryCache(true)

这里只是简单介绍部分使用,更多的内容请参考 按需加载 原则,并且可以参考ImageKnifeComponent源码或者根据文档自行探索实现。

接口说明

RequestOption用户配置参数

方法名入参接口描述
load(src: stringPixelMapResource)src:stringPixelMapResource用户加载图片源
setImageViewSize(imageSize: { width: number, height: number })imageSize:{width: number, height: number }传入显示图片组件的大小,变换的时候需要作为参考
diskCacheStrategy(strategy: DiskStrategy)strategy:DiskStrategy配置磁盘缓存策略 NONE SOURCE RESULT ALL AUTOMATIC
placeholder(src: PixelMapResource, func?: AsyncSuccess)src: PixelMapResource, func?: AsyncSuccess占位图,占位图回调数据ImageKnifeData
errorholder(src: PixelMapResource, func?: AsyncSuccess)src: PixelMapResource, func?: AsyncSuccess错误占位图,错误占位图回调数据ImageKnifeData
retryholder(src: PixelMapResource, func?: AsyncSuccess)src: PixelMapResource, func?: AsyncSuccess重试占位图,重试占位图回调数据ImageKnifeData
addListener(func: AsyncCallback)func: AsyncCallback配置整个监听回调,数据正常加载返回,加载失败返回错误信息
thumbnail(sizeMultiplier:number, func?: AsyncSuccess)sizeMultiplier:number, func?: AsyncSuccess设置缩略图比例,缩略图返回后,加载并展示缩略图
addProgressListener(func?: AsyncSuccess)func?: AsyncSuccess设置网络下载百分比监听,返回数据加载百分比数值
addAllCacheInfoCallback(func: IAllCacheInfoCallback)func: IAllCacheInfoCallback设置获取所有缓存信息监听
skipMemoryCache(skip: boolean)skip: boolean配置是否跳过内存缓存
retrieveDataFromCache(flag: boolean)flag: boolean配置仅从缓存中加载数据
signatureObjectKey自定义key

同时支持[图片变换相关]接口。

ImageKnife 启动器/门面类

方法名入参接口描述
call(request: RequestOption)request: RequestOption根据用户配置参数具体执行加载流程
preload(request: RequestOption)request: RequestOption根据用户配置参数具体执行预加载流程
pauseRequests()全局暂停请求
resumeRequests()全局恢复暂停

缓存策略相关

使用方法类型策略描述
request.diskCacheStrategy(new ALL())ALL表示既缓存原始图片,也缓存转换过后的图片
request.diskCacheStrategy(new AUTOMATIC())AUTOMATIC表示尝试对本地和远程图片使用适合的策略
request.diskCacheStrategy(new DATA())DATA表示只缓存原始图片
request.diskCacheStrategy(new NONE())NONE表示不缓存任何内容
request.diskCacheStrategy(new RESOURCE())RESOURCE表示只缓存转换过后的图片

AntiAliasing类型展示效果

使用方法类型策略描述
AntiAliasing.FIT_HIGHString图像抗锯齿设置为高画质
AntiAliasing.FIT_MEDIUMString图像抗锯齿设置为中画质
AntiAliasing.FIT_LOWString图像抗锯齿设置为低画质

ScaleType类型展示效果

使用方法类型策略描述
ScaleType.FIT_STARTint图像位于用户设置组件左上角显示,图像会缩放至全部展示
ScaleType.FIT_ENDint图像位于用户设置组件右下角显示,图像会缩放至全部展示
ScaleType.FIT_CENTERint图像位于用户设置组件居中,图像会缩放至全部展示
ScaleType.CENTERint图像居中展示,不缩放
ScaleType.CENTER_CROPint图像的宽高长度,短的部分缩放至组件大小,超出的全部裁剪
ScaleType.FIT_XYint图像拉伸至组件大小
ScaleType.CENTER_INSIDEint如果图像大于组件则执行FIT_CENTER,小于组件则CENTER
ScaleType.NONEint如果不想适配,直接展示原图大小

图片变换相关

使用方法类型相关描述
request.centerCrop()CenterCrop可以根据图片文件,目标显示大小,进行对应centerCrop
request.centerInside()CenterInside可以根据图片文件,目标显示大小,进行对应centerInside
request.fitCenter()FitCenter可以根据图片文件,目标显示大小,进行对应fitCenter
request.blur()BlurTransformation模糊处理(图片分辨率较大建议传递第二个参数将图片进行缩小)
request.brightnessFilter()BrightnessFilterTransformation亮度滤波器
request.contrastFilter()ContrastFilterTransformation对比度滤波器
request.cropCircle()CropCircleTransformation圆形剪裁显示
request.cropCircleWithBorder()CropCircleWithBorderTransformation圆环展示
request.cropSquare()CropSquareTransformation正方形剪裁
request.crop()CropTransformation自定义矩形剪裁
request.grayscale()GrayscaleTransformation灰度级转换
request.invertFilter()InvertFilterTransformation反转滤波器
request.pixelationFilter()PixelationFilterTransformation像素化滤波器
request.rotateImage()RotateImageTransformation图片旋转
request.roundedCorners()RoundedCornersTransformation圆角剪裁
request.sepiaFilter()SepiaFilterTransformation乌墨色滤波器
request.sketchFilter()SketchFilterTransformation素描滤波器
request.mask()MaskTransformation遮罩
request.swirlFilter()SwirlFilterTransformation扭曲滤波器
request.kuwaharaFilter()KuwaharaFilterTransform桑原滤波器
request.toonFilter()ToonFilterTransform动画滤波器
request.vignetteFilter()VignetteFilterTransform装饰滤波器

setLruCacheSize

setLruCacheSize(size: number,memory:number): void

设置图片文件缓存的大小上限,size单位为张数,memory单位为字节,提升再次加载同源图片的加载速度,特别是对网络图源会有较明显提升。 如果不设置则默认为100张,100MB。缓存采用内置的LRU策略。 size为0则代表不限制缓存张数,memory为0则代表不限制缓存大小。 建议根据应用实际需求,设置合理缓存上限,数字过大可能导致内存占用过高,可能导致OOM异常。

参数:

参数名类型必填说明
sizenumber图片文件的缓存张数,单位为张。只支持正整数,0
memorynumber图片文件的缓存大小,单位为字节。只支持正数,0

示例:

//EntryAbility.ets
import { InitImageKnife } from '...imageknife'
export default class EntryAbility extends UIAbility {
  onWindowStageCreate(windowStage: window.WindowStage) { 
    InitImageKnife.init(this.context);
    let imageKnife: ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife()
    if (imageKnife != undefined) {
      //设置全局内存缓存大小张数
      imageKnife.setLruCacheSize(100, 100 * 1204 * 1024)
    }
  }
}

约束与限制

在下述版本验证通过: DevEco Studio 4.1(4.1.3.520)--SDK:API11( 4.1.0.63) DevEco Studio 4.1(4.1.3.418)--SDK:API11( 4.1.0.56) DevEco Studio 4.1(4.1.3.322)--SDK:API11( 4.1.0.36) DevEco Studio 4.0(4.0.3.700)--SDK:API10( 4.0.10.15)

HSP场景适配:

在使用ImageKnifeComponent进行加载图片时, 提供的ImageKnifeOption配置类新增了可选参数context, 在HSP场景下需要传入正确的context, 才能保证三方库后续正确获取Resource资源。

在使用RquestOption进行加载图片时, 提供的RquestOption配置类新增了接口setModuleContext(moduleCtx:common.UIAbilityContext), 在HSP场景下需要传入正确的context, 才能保证三方库后续正确获取Resource资源。

非HSP场景不影响原功能, ImageKnifeOption配置类新增的可选参数context可以不传, RquestOption配置类新增的接口可以不调用。

更多鸿蒙开发知识已经更新在gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md前往参考。

HarmonyOS与OpenHarmony技术路线曲线图1.png

遗留问题

1.目前只支持一种图片变换效果。

2.目前svg和gif动图不支持变换效果。

补充说明

SVG标签说明

使用版本为(SVG)1.1,当前支持的标签列表有:

  • a
  • circla
  • clipPath
  • defs
  • ellipse
  • feBlend
  • feColorMatrix
  • feComposite
  • feDiffuseLighting
  • feDisplacementMap
  • feDistantLight
  • feFlood
  • feGaussianBlur
  • feImage
  • feMorphology
  • feOffset
  • fePointLight
  • feSpecularLighting
  • feSpotLight
  • feTurbulence
  • filter
  • g
  • image
  • line
  • linearGradient
  • mask
  • path
  • pattern
  • polygon
  • polyline
  • radialGradient
  • rect
  • stop
  • svg
  • text
  • textPath
  • tspan

鸿蒙值得开发者入行

为什么这么说?市场是决定人力需求的,数据说话最管用:

1、鸿蒙其全栈自研,头部大厂商都陆续加入合作开发鸿蒙原生应用——人才需求上涨

2、鸿蒙作为新系统、新技术,而现在市面上技术人才少——高薪招聘开启

3、鸿蒙1+8+N生态,不仅只有应用开发;还有车载、数码、智能家居、家电等——就业范围广

4、纯血鸿蒙,目前没有多少人熟悉。都处于0基础同一起跑线——无行业内卷

开发者最需要什么?岗位多、薪资高、不内卷、行业竞争低。而当下的鸿蒙恰恰符合要求。

那么这么好的鸿蒙岗位,应聘要求都很高吧?其实不然鸿蒙作为新出的独立系统,其源头上大家都处于同一水平线上,一开始的技术要求都不会很高,毕竟面试官也是刚起步学习。招聘要求示例:

从信息看出,几乎应职要求是对标有开发经验的人群。可以说鸿蒙对开发者非常友好,尽管上面没提鸿蒙要求,但是面试都会筛选具有鸿蒙开发技能的人。我们程序员都知道学习开发技术,最先是从语言学起,鸿蒙语言有TS、ArkTS等语法,那么除了这些基础知识之外,其核心技术点有那些呢?下面就用一张整理出的鸿蒙学习路线图表示:

从上面的OpenHarmony技术梳理来看,鸿蒙的学习内容也是很多的。现在全网的鸿蒙学习文档也是非常的少,下面推荐一些:完整内容可在头像页保存,或这qr23.cn/AKFP8k甲助力

内容包含:

  • ArkTS
  • 声明式ArkUI
  • 多媒体
  • 通信问题
  • 系统移植
  • 系统裁剪
  • FW层的原理
  • 各种开发调试工具
  • 智能设备开发
  • 分布式开发等等。

这些就是对往后开发者的前景分享,希望大家多多点赞关注喔!

  • 15
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值