图片是UI界面的重要元素之一, 图片加载速度及成果间接影响利用体验。ArkUI开发框架提供了丰盛的图像处理能力,如图像解码、图像编码、图像编辑及根本的位图操作等,满足了开发者日常开发所需。
但随着产品需要的日益增长,根本的图像处理能力已不能胜任某些比较复杂的利用场景,如无奈间接获取缓存图片、无奈配置占位图、无奈进行自定义PixelMap图片变换等。
为加强ArkUI开发框架的图像处理能力,ImageKnife组件应运而生。本期咱们将为大家带来ImageKnife的介绍。
一、ImageKnife简介
ImageKnife是一个参考Glide框架进行设计,并基于eTS语言实现的图片解决组件。它能够让开发者能轻松且高效地进行图片开发。
注:Glide是一个疾速高效的图片加载库,重视于平滑的滚动,提供了易用的API,高性能、可扩大的图片解码管道,以及主动的资源池技术。
性能方面,ImageKnife提供了自定义图片变换、占位图等图片解决能力,简直满足了开发者进行图片解决的所有需要。
性能方面,ImageKnife采纳LRU策略实现二级缓存,可灵便配置,无效缩小内存耗费,晋升了利用性能。
应用方面,ImageKnife封装了一套残缺的图片加载流程,开发者只需依据ImageKnifeOption配置相干信息即可实现图片的开发,升高了开发难度,晋升了开发效率。如图1所示,是ImageKnife加载图片的整体流程。
$$图1 ImageKnife加载图片整体流程$$
二、ImageKnife实现原理
上面咱们将为大家介绍ImageKnife加载图片过程中每个环节的实现原理,让大家更粗浅地意识ImageKnife组件。图2是ImageKnife加载图片的时序图:
$$图2 ImageKnife加载图片的时序图$$
1. 用户配置信息
在加载图片前,用户需依据本身需要配置相应的参数,包含图片门路、图片大小、占位图及缓存策略等。ImageKnife提供了RequestOption类,用于封装用户配置信息的接口,如图3所示列举了局部接口供大家参考:
$$图3 用户配置参数$$
通过ImageKnifeExecute()办法获取用户配置信息,而后执行ImageKnife.call(request),正式启动图片加载工作。相干实现代码如下:
imageKnifeExecute() { // 首先须要确保获取ImageKnife单例对象 if(ImageKnife){ }else{ ImageKnife = globalThis.exports.default.data.imageKnife; } // 生成配置信息requestOption let request = new RequestOption(); // 配置必要信息和回调 this.configNecessary(request); // 配置缓存相干信息 this.configCacheStrategy(request); // 配置显示信息和回调 this.configDisplay(request); // 启动ImageKnife执行申请 ImageKnife.call(request);}
2. 加载图片
加载图片过程是ImageKnife组件的外围局部,如图4所示,蕴含占位图填充、缓存实现及图片解码三个环节。上面咱们将为大家别离介绍每个环节的实现。
$$图4图片加载过程$$
(1) 占位图填充
占位图就是图片加载过程中页面上的过渡成果,通常表现形式是在页面上待加载区域填充灰色的占位图,能够使得页面框架不会因为加载失败而变形。ImageKnife提供了占位图性能,开发者可在RequestOption中配置是否启动占位图工作。
如图5所示是占位图工作流程,执行图片加载工作后,占位图会填充加载页面。如果图片解析胜利则将页面上填充的占位图替换为待加载的图片。如果图片解析失败,则将页面上填充的占位图替换为“图片解析失败占位图”。
$$图5 占位图工作流程$$
相干实现代码如下:
// 占位图解析胜利placeholderOnComplete(imageKnifeData: ImageKnifeData) {// 主图未加载胜利,并且未加载失败 显示占位图 主图加载胜利或者加载失败后=>不展现占位图 if (!this.loadMainReady && !this.loadErrorReady && !this.loadThumbnailReady) { this.placeholderFunc(imageKnifeData) }}// 加载失败 占位图解析胜利errorholderOnComplete(imageKnifeData: ImageKnifeData) { // 如果有谬误占位图 先解析并保留在RequestOption中 等到加载失败时候进行调用 this.errorholderData = imageKnifeData; if (this.loadErrorReady) { this.errorholderFunc(imageKnifeData) }}
(2) 缓存实现
缓存是图片加载过程中最要害的环节,缓存机制间接影响了图片加载速度及图片滚动成果。开发者可通过以下办法来灵便配置缓存策略,
$$图6 缓存策略API$$
为了保障图片的加载速度,ImageKnife通过应用Least Recently Used(最近起码应用)清空策略来实现内存缓存及磁盘缓存。
如图7所示,在图片加载过程中,CPU会首先读取内存缓存中的数据,如果读取到图片资源则间接显示图片,否则读取磁盘缓存数据。如果在磁盘缓存上依然没有读取到数据,则可断定为该图片为网络图片,这时须要将网络图片解码后再进行显示(前面章节会具体介绍),并将解码后的图片文件缓存至磁盘。
$$图7 图片缓存过程$$
上面咱们将别离介绍两种缓存机制的具体实现:
① 内存缓存
内存缓存,就是指以后程序运行内存调配的长期存储器,当咱们应用ImageKnife加载图片时,这张图片会被缓存到内存当中,只有在它还没从内存中被革除之前,下次再加载这张图片都会间接从内存中读取,而不必从新从网络或硬盘上读取,大幅度晋升图片的加载效率。
ImageKnife内存缓存的实现,需管制最大空间(maxsize),以及目前占用空间(size),相干实现代码如下:
// 移除较少应用的缓存数据trimToSize(tempsize: number) { while (true) { if (tempsize < 0) { this.map.clear() this.size = 0 break } if (this.size <= tempsize || this.map.isEmpty()) { break } var delkey = this.map.getFirstKey() this.map.remove(delkey) this.size-- }}// 缓存数据最大值maxSize(): number{ return this.maxsize}// 设置缓存数据量最大值resize(maxsize: number) { if (maxsize < 0) { throw new Error('maxsize <0 & maxsize invalid'); } this.maxsize = maxsize this.trimToSize(maxsize)}// 革除缓存evicAll() { this.trimToSize(-1)}
② 磁盘缓存
默认状况下,磁盘缓存的是解码后的图片文件,需避免利用反复从网络或其余中央下载和读取数据。ImageKnife磁盘缓存的实现,次要依附journal文件对缓存数据进行保留,保障程序磁盘缓存内容的长久化问题。
相干实现代码如下:
//读取journal文件的缓存数据readJournal(path: string) { var fileReader = new FileReader(path) var line: string = '' while (!fileReader.isEnd()) { line = fileReader.readLine() line = line.replace('\n', '').replace('\r', '') this.dealwithJournal(line) } this.fileUtils.deleteFile(this.journalPathTemp) this.trimToSize()}//依据LRU算法删除多余缓存数据private trimToSize() { while (this.size > this.maxSize) { var tempkey: string = this.cacheMap.getFirstKey() var fileSize = this.fileUtils.getFileSize(this.dirPath + tempkey) if (fileSize > 0) { this.size = this.size - fileSize } this.fileUtils.deleteFile(this.dirPath + tempkey) this.cacheMap.remove(tempkey) this.fileUtils.writeData(this.journalPath, 'remove ' + tempkey + '\n') }}//革除所有disk缓存数据cleanCacheData() { var length = this.cacheMap.size() for (var index = 0; index < length; index++) { this.fileUtils.deleteFile(this.dirPath + this.cacheMap[index]) } this.fileUtils.deleteFile(this.journalPath) this.cacheMap.clear() this.size = 0}
(3) 图片解码
当咱们应用ImageKnife去加载一张图片的时候,并不是将原始图片间接显示进去,而是会进行图片解码后再显示到页面。图片解码就是将不同格局的图片(包含JPEG、PNG、GIF、WebP、BMP)解码成对立格局的PixelMap图片文件。
ImageKnife的图片解码能力依赖的是ArkUI开发框架提供的ImageSource解码能力。通过import image from '@ohos.multimedia.image'导入ArkUI开发框架的图片能力,并调用createImageSource()办法获取,实现代码如下:
import image from '@ohos.multimedia.image'export class TransformUtils { static centerCrop(buf: ArrayBuffer, outWidth: number, outHeihgt: number, callback?: AsyncTransform<Promise<PixelMap>>) { // 创立媒体解码imageSource var imageSource = image.createImageSource(buf as any); // 获取图片信息 imageSource.getImageInfo() .then((p) => { var sw; var sh; var scale; var pw = p.size.width; var ph = p.size.height; // 依据centerCrop规定管制缩放比例 if (pw == outWidth && ph == outHeihgt) { sw = outWidth; sh = outHeihgt; } else { if (pw * outHeihgt > outWidth * ph) { scale = outHeihgt / ph; } else { scale = outWidth / pw; } sw = pw * scale; sh = ph * scale; } var options = { editable: true, rotate: 0, desiredRegion: { size: { width: sw, height: sh }, x: pw / 2 - sw / 2, y: ph / 2 - sh / 2, }, } if (callback) { // 回调,创立相干配置pixelmap callback('', imageSource.createPixelMap(options)); } }) .catch((error) => { callback(error, null); }) }}
3. 显示图片
获取到PixelMap解码文件后,接下来就是将它渲染到利用界面上。ImageKnife的图片渲染能力依赖的是ArkUI开发框架提供的Image组件的渲染能力。因为eTS是申明式的,咱们无奈间接取得Image组件的对象,须要依赖ArkUI开发框架的@State能力绑定输出参数,在扭转属性对象之后,告诉UI组件从新渲染,达到图片显示的成果。
相干代码如下:
@Componentexport struct ImageKnifeComponent { @Watch('watchImageKnifeOption') @Link imageKnifeOption: ImageKnifeOption; @State imageKnifePixelMapPack: PixelMapPack = new PixelMapPack(); @State imageKnifeResource: Resource = $r('app.media.icon_loading') @State imageKnifeString: string = '' @State normalPixelMap: boolean = false; @State normalResource: boolean = true; previousData: ImageKnifeData = null; nowData: ImageKnifeData = null; build() { Stack() { //Image组件配置 Image(this.normalPixelMap ? this.imageKnifePixelMapPack.pixelMap : (this.normalResource ? this.imageKnifeResource : this.imageKnifeString)) .objectFit(this.imageKnifeOption.imageFit ? this.imageKnifeOption.imageFit : ImageFit.Fill) .visibility(this.imageVisible) .width(this.imageWidth) .height(this.imageHeight) } } //必要的用户配置和回调办法 configNecessary(request: RequestOption){ request.load(this.imageKnifeOption.loadSrc) .addListener((err, data) => { console.log('request.load callback') this.imageKnifeChangeSource(data) this.animateTo('image'); return false; }) if (this.imageKnifeOption.size) { request.setImageViewSize(this.imageKnifeOption.size) } } // imageknife 第一次启动和数据刷新后从新发送申请 imageKnifeExecute() { let request = new RequestOption(); this.configNecessary(request); this.configCacheStrategy(request); this.configDisplay(request); ImageKnife.call(request); } //返回数据Image渲染展现图片 imageKnifeSpecialFixed(data:ImageKnifeData) { if (data.isPixelMap()) { this.displayPixelMap(data); } else if (data.isString()) { this.displayString(data); } else if (data.isResource()) { this.displayResource(data); } else { } }}
注:@State装璜的变量是组件外部的状态数据,当这些状态数据被批改时,将会调用所在组件的build办法进行UI刷新。
三、ImageKnife实战
通过上文的介绍,置信大家对ImageKnife组件有了粗浅的理解。上面咱们将创立一个ImageKnife_Test我的项目,为大家展现ArkUI开发框架中ImageKnife组件的应用。通过将ImageKnife组件下载至我的项目中,而后依据ImageKnifeOption配置相干信息,即可实现GIF图片的加载。
1. 创立我的项目
如图8所示,在DevEco Studio中新建ImageKnife_Test我的项目,我的项目类型抉择Application,语言选择eTS,点击Finish实现创立。
$$图8 创立我的项目$$
2. 增加依赖
胜利创立我的项目后,接下来就是将ImageKnife组件下载至我的项目中。
首先,咱们需找到.npmrc 配置文件,并在文件中增加 @ohos 的scope仓库地址:@ohos:registry=https://repo.harmonyos.com/npm/,如图9所示:
$$图9 增加 scope仓库地址$$
配置好npm仓库地址后,如图10所示,在DevEco Studio的底部导航栏,点击“Terminal”(快捷键Alt+F12),键入命令:npm install @ohos/imageknife并回车,此时ImageKnife组件会被主动下载至我的项目中。下载实现后工程根目录下会生成node_modules/@ohos/imageknife目录。
$$图10 下载至我的项目$$
3. 编写逻辑代码
ImageKnife组件胜利下载至我的项目中后,接下来就是逻辑代码编写,这里咱们将为大家介绍两种应用形式:
形式一:首先初始化全局ImageKnife实例,而后在app.ets中调用ImageKnife.with()进行初始化,相干代码如下:
import {ImageKnife} from '@ohos/imageknife'export default { data: { imageKnife: {} // ImageKnife }, onCreate() { this.data.imageKnife = ImageKnife.with(); }, onDestroy() { },}
而后在页面index.ets中应用ImageKnife,相干代码如下:
@Entry@Componentstruct Index { build() { } // 页面初始化实现,生命周期回调函数中 进行调用ImageKnife aboutToAppear() { let requestOption = new RequestOption(); requestOptin.load($r('app.media.IceCream')) .addListener((err,data) => { //加载胜利/失败回调监听 }) ... ImageKnife.call(requestOption) }}var ImageKnife;var defaultTemp = globalThis.exports.defaultif (defaultTemp != undefined) { ImageKnife = defaultTemp.data.imageKnife;}(左右滑动,查看更多)形式二: 在index.ets中,间接应用ImageKnifeOption作为入参,并配合自定义组件ImageKnifeComponent应用,相干代码如下:import {ImageKnifeOption} from '@ohos/imageknife'@Entry@Componentstruct Index { @State imageKnifeOption1: ImageKnifeOption = { loadSrc: $r('app.media.gifSample'), size: { width: 300, height: 300 }, placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed') }; build() { Scroll() { Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { ImageKnifeComponent({ imageKnifeOption: $imageKnifeOption1 }) } } .width('100%') .height('100%') }}
以上就是本期全部内容,祝贺大家花几分钟工夫播种了一个实用的组件。心愿宽广开发者能利用这个弱小的开源组件开发出更多精美的利用。