本期我們給大家帶來的是開發者周黎生的分享,希望能給你的HarmonyOS開發之旅帶來啟發~
圖片是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 = https://www.cnblogs.com/HarmonyOSDev/archive/2022/04/28/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組件重新渲染,達到圖片顯示的效果,
相關代碼如下:
@Component
export 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
@Component
struct 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.default
if (defaultTemp != undefined) {
ImageKnife = defaultTemp.data.imageKnife;
}
方式二:在index.ets中,直接使用ImageKnifeOption作為入參,并配合自定義組件ImageKnifeComponent使用,相關代碼如下:
import {ImageKnifeOption} from '@ohos/imageknife'
@Entry
@Component
struct 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%')
}
}
以上就是本期全部內容,恭喜大家花幾分鐘時間識訓了一個實用的組件,希望廣大開發者能利用這個強大的開源組件開發出更多精美的應用,

搜索
復制
<iframe></iframe>轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/467109.html
標籤:其他
上一篇:Java在eclipse中運行并且會編譯,但不會在cmd上執行,但仍然在eclipse中運行。我怎樣才能讓它在cmd中執行?
下一篇:RecyclerView顯示串列
