圖片是 UI 界面的重要元素之一,圖片加載速度及效果直接影響應(yīng)用體驗。ArkUI 開發(fā)框架提供了豐富的圖像處理能力,如圖像解碼、圖像編碼、圖像編輯及基本的位圖操作等,滿足了開發(fā)者日常開發(fā)所需。但隨著產(chǎn)品需求的日益增長,基本的圖像處理能力已不能勝任某些比較復(fù)雜的應(yīng)用場景,如無法直接獲取緩存圖片、無法配置占位圖、無法進(jìn)行自定義 PixelMap 圖片變換等。為增強(qiáng) ArkUI 開發(fā)框架的圖像處理能力,ImageKnife 組件應(yīng)運(yùn)而生,本期我們將為大家?guī)?ImageKnife 的介紹。
一、ImageKnife簡介
ImageKnife 是一個參考 Glide 框架進(jìn)行設(shè)計,并基于 eTS 語言實(shí)現(xiàn)的圖片處理組件,讓開發(fā)者能輕松且高效地進(jìn)行圖片開發(fā)。而 Glide 是一個快速高效的圖片加載庫,它注重于平滑的滾動,提供了易用的 API,高性能、可擴(kuò)展的圖片解碼管道,以及自動的資源池技術(shù)。
功能方面,ImageKnife 提供了自定義圖片變換、占位圖等圖片處理能力,幾乎滿足了開發(fā)者進(jìn)行圖片處理的一切需求。性能方面,ImageKnife 采用 LRU 策略實(shí)現(xiàn)二級緩存,可靈活配置,有效減少內(nèi)存消耗,提升了應(yīng)用性能。使用方面,ImageKnife 封裝了一套完整的圖片加載流程,開發(fā)者只需根據(jù) ImageKnifeOption 配置相關(guān)信息即可完成圖片的開發(fā),降低了開發(fā)難度,提升了開發(fā)效率。如圖 1 所示,是 ImageKnife 加載圖片的整體流程。

二、ImageKnife實(shí)現(xiàn)原理
下面我們將為大家介紹 ImageKnife 加載圖片過程中每個環(huán)節(jié)的實(shí)現(xiàn)原理,讓大家更深刻地認(rèn)識 ImageKnife 組件。圖 2 是 ImageKnife 加載圖片的時序圖:

圖2 ImageKnife加載圖片的時序圖
2.1 用戶配置信息
在加載圖片前,用戶需根據(jù)自身需求配置相應(yīng)的參數(shù),包括圖片路徑、圖片大小、占位圖及緩存策略等。ImageKnife 提供了 RequestOption 類,用于封裝用戶配置信息的接口,如圖 3 所示列舉了部分接口供大家參考:

圖3 用戶配置參數(shù)
通過 imageKnifeExecute() 方法獲取用戶配置信息,然后執(zhí)行 ImageKnife.call(request),正式啟動圖片加載任務(wù)。相關(guān)實(shí)現(xiàn)代碼如下:
imageKnifeExecute() {// 首先需要確保獲取ImageKnife單例對象if(ImageKnife){}else{ImageKnife = globalThis.exports.default.data.imageKnife;}// 生成配置信息requestOptionlet request = new RequestOption();// 配置必要信息和回調(diào)this.configNecessary(request);// 配置緩存相關(guān)信息this.configCacheStrategy(request);// 配置顯示信息和回調(diào)this.configDisplay(request);// 啟動ImageKnife執(zhí)行請求ImageKnife.call(request);}
2.2 加載圖片
加載圖片過程是 ImageKnife 組件的核心部分,如圖 4 所示,包含占位圖填充、緩存實(shí)現(xiàn)及圖片解碼三個環(huán)節(jié)。下面我們將為大家分別介紹每個環(huán)節(jié)的實(shí)現(xiàn)。

2.2.1 占位圖填充
占位圖就是圖片加載過程中頁面上的過渡效果,通常表現(xiàn)形式是在頁面上待加載區(qū)域填充灰色的占位圖,可以使得頁面框架不會因為加載失敗而變形。ImageKnife 提供了占位圖功能,開發(fā)者可在 RequestOption 中配置是否啟動占位圖任務(wù)。如圖 5 所示是占位圖工作流程,執(zhí)行圖片加載任務(wù)后,占位圖會填充加載頁面。如果圖片解析成功則將頁面上填充的占位圖替換為待加載的圖片。如果圖片解析失敗,則將頁面上填充的占位圖替換為“圖片解析失敗占位圖”。

圖5 占位圖工作流程
相關(guān)實(shí)現(xiàn)代碼如下:
// 占位圖解析成功placeholderOnComplete(imageKnifeData: ImageKnifeData) {// 主圖未加載成功,并且未加載失敗 顯示占位圖 主圖加載成功或者加載失敗后=>不展示占位圖if (!this.loadMainReady && !this.loadErrorReady && !this.loadThumbnailReady) {this.placeholderFunc(imageKnifeData)}}// 加載失敗 占位圖解析成功errorholderOnComplete(imageKnifeData: ImageKnifeData) {// 如果有錯誤占位圖 先解析并保存在RequestOption中 等到加載失敗時候進(jìn)行調(diào)用this.errorholderData = imageKnifeData;if (this.loadErrorReady) {this.errorholderFunc(imageKnifeData)}
2.2.2 緩存實(shí)現(xiàn)
緩存是圖片加載過程中最關(guān)鍵的環(huán)節(jié),緩存機(jī)制直接影響了圖片加載速度及圖片滾動效果,開發(fā)者可通過以下方法來靈活配置緩存策略。

圖6 緩存策略API
為了保障圖片的加載速度,ImageKnife 通過使用 Least Recently Used(最近最少使用)清空策略來實(shí)現(xiàn)內(nèi)存緩存及磁盤緩存。如圖 7 所示,在圖片加載過程中,CPU 會首先讀取內(nèi)存緩存中的數(shù)據(jù),如果讀取到圖片資源則直接顯示圖片,否則讀取磁盤緩存數(shù)據(jù)。如果在磁盤緩存上仍然沒有讀取到數(shù)據(jù),則可判定為該圖片為網(wǎng)絡(luò)圖片,這時需要將網(wǎng)絡(luò)圖片解碼后再進(jìn)行顯示(后面章節(jié)會詳細(xì)介紹),并將解碼后的圖片文件緩存至磁盤。

圖7 圖片緩存過程
下面我們將分別介紹兩種緩存機(jī)制的具體實(shí)現(xiàn)。
(1)內(nèi)存緩存
內(nèi)存緩存,就是指當(dāng)前程序運(yùn)行內(nèi)存分配的臨時存儲器,當(dāng)我們使用 ImageKnife 加載圖片時,這張圖片會被緩存到內(nèi)存當(dāng)中,只要在它還沒從內(nèi)存中被清除之前,下次再加載這張圖片都會直接從內(nèi)存中讀取,而不用重新從網(wǎng)絡(luò)或硬盤上讀取,大幅度提升圖片的加載效率。ImageKnife 內(nèi)存緩存的實(shí)現(xiàn),需控制最大空間(maxsize),以及目前占用空間(size),相關(guān)實(shí)現(xiàn)代碼如下:
// 移除較少使用的緩存數(shù)據(jù)trimToSize(tempsize: number) {while (true) {if (tempsize < 0) {this.map.clear()this.size = 0break}if (this.size <= tempsize || this.map.isEmpty()) {break}var delkey = this.map.getFirstKey()this.map.remove(delkey)this.size--}}// 緩存數(shù)據(jù)最大值maxSize(): number{return this.maxsize}// 設(shè)置緩存數(shù)據(jù)量最大值resize(maxsize: number) {if (maxsize < 0) {throw new Error('maxsize <0 & maxsize invalid');}this.maxsize = maxsizethis.trimToSize(maxsize)}// 清除緩存evicAll() {this.trimToSize(-1)}
(2)磁盤緩存
默認(rèn)情況下,磁盤緩存的是解碼后的圖片文件,需防止應(yīng)用重復(fù)從網(wǎng)絡(luò)或其他地方下載和讀取數(shù)據(jù)。ImageKnife 磁盤緩存的實(shí)現(xiàn),主要依靠 journal 文件對緩存數(shù)據(jù)進(jìn)行保存,保證程序磁盤緩存內(nèi)容的持久化問題。相關(guān)實(shí)現(xiàn)代碼:
//讀取journal文件的緩存數(shù)據(jù)readJournal(path: string) {var fileReader = new FileReader(path)var line: string = ''while (!fileReader.isEnd()) {line = fileReader.readLine()line = line.replace(' ', '').replace(' ', '')this.dealwithJournal(line)}this.fileUtils.deleteFile(this.journalPathTemp)this.trimToSize()}//根據(jù)LRU算法刪除多余緩存數(shù)據(jù)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 + ' ')}}//清除所有disk緩存數(shù)據(jù)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}
2.2.3 圖片解碼
當(dāng)我們使用 ImageKnife 去加載一張圖片的時候,并不是將原始圖片直接顯示出來,而是會進(jìn)行圖片解碼后再顯示到頁面。圖片解碼就是將不同格式的圖片(包括 JPEG、PNG、GIF、WebP、BMP)解碼成統(tǒng)一格式的 PixelMap 圖片文件。ImageKnife 的圖片解碼能力依賴的是 ArkUI 開發(fā)框架提供的 ImageSource 解碼能力。通過 import image from '@ohos.multimedia.image'導(dǎo)入 ArkUI 開發(fā)框架的圖片能力,并調(diào)用 createImageSource() 方法獲取,實(shí)現(xiàn)代碼如下:
import image from '@ohos.multimedia.image'export class TransformUtils {static centerCrop(buf: ArrayBuffer, outWidth: number, outHeihgt: number,callback?: AsyncTransform>) { // 創(chuàng)建媒體解碼imageSourcevar 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;// 根據(jù)centerCrop規(guī)則控制縮放比例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) {// 回調(diào),創(chuàng)建相關(guān)配置pixelmapcallback('', imageSource.createPixelMap(options));}}).catch((error) => {callback(error, null);})}}
2.3 顯示圖片
獲取到 PixelMap 解碼文件后,接下來就是將它渲染到應(yīng)用界面上。ImageKnife 的圖片渲染能力依賴的是 ArkUI 開發(fā)框架提供的 image 組件的渲染能力。由于 eTS 是聲明式的,我們無法直接獲得 Image 組件的對象,需要依賴 ArkUI 開發(fā)框架的 @State 能力綁定輸入?yún)?shù),在改變屬性對象之后,通知 UI 組件重新渲染,達(dá)到圖片顯示的效果,相關(guān)代碼如下:注:@State 裝飾的變量是組件內(nèi)部的狀態(tài)數(shù)據(jù),當(dāng)這些狀態(tài)數(shù)據(jù)被修改時,將會調(diào)用所在組件的 build 方法進(jìn)行 UI 刷新。export struct ImageKnifeComponent {imageKnifeOption: ImageKnifeOption;imageKnifePixelMapPack: PixelMapPack = new PixelMapPack();imageKnifeResource: Resource = $r('app.media.icon_loading')imageKnifeString: string = ''normalPixelMap: boolean = false;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)}}//必要的用戶配置和回調(diào)方法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 第一次啟動和數(shù)據(jù)刷新后重新發(fā)送請求imageKnifeExecute() {let request = new RequestOption();this.configNecessary(request);this.configCacheStrategy(request);this.configDisplay(request);ImageKnife.call(request);}//返回數(shù)據(jù)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 {}}}
三、ImageKnife實(shí)戰(zhàn)
通過上文的介紹,相信大家對 ImageKnife 組件有了深刻的了解。下面我們將創(chuàng)建一個 ImageKnife_Test 項目,為大家展示 ArkUI 開發(fā)框架中 ImageKnife 組件的使用。通過將 ImageKnife 組件下載至項目中,然后根據(jù) ImageKnifeOption 配置相關(guān)信息,即可完成 GIF 圖片的加載。3.1 創(chuàng)建項目
如圖 8 所示,在 DevEco Studio 中新建 ImageKnife_Test 項目,項目類型選擇 Application,語言選擇 eTS,點(diǎn)擊 Finish 完成創(chuàng)建。

圖8 創(chuàng)建項目
3.2 添加依賴
成功創(chuàng)建項目后,接下來就是將 ImageKnife 組件下載至項目中。
首先,我們需找到 .npmrc 配置文件,并在文件中添加 @ohos 的 scope 倉庫地址:@ohos:registry=https://repo.harmonyos.com/npm/,如圖 9 所示:

圖9 添加 scope倉庫地址
配置好 npm 倉庫地址后,如圖 10 所示,在 DevEco Studio 的底部導(dǎo)航欄,點(diǎn)擊“Terminal”(快捷鍵 Alt+F12),鍵入命令:npm install @ohos/imageknife 并回車,此時 ImageKnife 組件會被自動下載至項目中。下載完成后工程根目錄下會生成 node_modules/@ohos/imageknife 目錄。

圖10 下載至項目
3.3 編寫邏輯代碼
ImageKnife 組件成功下載至項目中后,接下來就是邏輯代碼編寫,這里我們將為大家介紹兩種使用方式:
方式一:首先初始化全局 ImageKnife 實(shí)例,然后在 app.ets 中調(diào)用 ImageKnife.with() 進(jìn)行初始化,相關(guān)代碼如下:
方式二:在 index.ets 中,直接使用 ImageKnifeOption 作為入?yún)?,并配合自定義組件 ImageKnifeComponent 使用,相關(guān)代碼如下:import {ImageKnife} from '@ohos/imageknife'export default {data: {imageKnife: {} // ImageKnife},onCreate() {this.data.imageKnife = ImageKnife.with();},onDestroy() {},}然后在頁面index.ets中使用imageknife,相關(guān)代碼如下:@Entry@Componentstruct Index {build() {}// 頁面初始化完成,生命周期回調(diào)函數(shù)中 進(jìn)行調(diào)用ImageKnifeaboutToAppear() {let requestOption = new RequestOption();requestOptin.load($r('app.media.IceCream')).addListener((err,data) => {//加載成功/失敗回調(diào)監(jiān)聽})...ImageKnife.call(requestOption)}}var ImageKnife;var defaultTemp = globalThis.exports.defaultif (defaultTemp != undefined) {ImageKnife = defaultTemp.data.imageKnife;}
以上就是本期全部內(nèi)容,恭喜大家又花幾分鐘時間收獲了一個實(shí)用的組件。希望廣大開發(fā)者能利用這個強(qiáng)大的開源組件開發(fā)出更多精美的應(yīng)用。同時,我們會陸續(xù)更新實(shí)用組件的解析,敬請期待! 審核編輯 :李倩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%')}}
-
框架
+關(guān)注
關(guān)注
0文章
404瀏覽量
18445 -
組件
+關(guān)注
關(guān)注
1文章
575瀏覽量
19028
原文標(biāo)題:ImageKnife組件,讓小白也能輕松搞定圖片開發(fā)
文章出處:【微信號:gh_e4f28cfa3159,微信公眾號:OpenAtom OpenHarmony】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
各種顯示技術(shù)簡介
比較器的簡介分類
VDMA IP核簡介
國密系列算法簡介及SM4算法原理介紹
risc-v P擴(kuò)展(一) P指令集簡介
AES加密模式簡介與對比分析
AXI GPIO擴(kuò)展e203 IO口簡介
Perforce P4產(chǎn)品簡介:無限擴(kuò)展+全球協(xié)作+安全管控+工具集成
FPC組成簡介及工程設(shè)計規(guī)范
【直播預(yù)告】《實(shí)時操作系統(tǒng)應(yīng)用技術(shù)—基于RT-Thread與ARM的編程實(shí)踐》教學(xué)脈絡(luò)及資源簡介
藍(lán)牙LE Audio技術(shù)簡介和優(yōu)勢分析
NVMe協(xié)議簡介2
物聯(lián)網(wǎng)單燈控制器產(chǎn)品簡介
ImageKnife簡介
評論