ImageKnife組件,讓小白也能輕鬆搞定圖片開發

来源:https://www.cnblogs.com/HarmonyOSDev/archive/2022/04/28/16202158.html
-Advertisement-
Play Games

為增強ArkUI開發框架的圖像處理能力,ImageKnife組件應運而生。本期我們將為大家帶來ImageKnife的介紹。 ...


本期我們給大家帶來的是開發者周黎生的分享,希望能給你的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 = 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%')
  }
}


以上就是本期全部內容,恭喜大家花幾分鐘時間收穫了一個實用的組件。希望廣大開發者能利用這個強大的開源組件開發出更多精美的應用。

 

 

搜索

複製


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 本文主要介紹ArcGis的ArcEngine開發,學習時,我們需要放下心裡障礙,那就是Gis開發只是普通的軟體開發,並不需要專業的GIS知識,就是非常普通的,調用相關的C++開發的COM組件。 開發環境:VS2017。 ArcEngine版本:10.1。 基礎學習 正式使用ArcGis之前,需 ...
  • 本文詳細的講解了FFT的實現,簡直是手把手了。我也是慢慢學過來的,知道網上的教程對於初學者不是很友好,所以決定自己寫一份博客來記錄下來我的經驗 ...
  • 本文例子參考《STM32單片機開發實例——基於Proteus虛擬模擬與HAL/LL庫》 源代碼:https://github.com/LanLinnet/STM33F103R6 項目要求 實現流水燈效果。 硬體設計 在第一節的基礎上,在Proteus中添加電路如下圖所示,其中我們添加了一個排阻RX8 ...
  • 如何基於Xcode搭建OpenCV開發環境 我的開發平臺是MacBook Pro (13-inch, M1, 2020),版本11.4,碩士課題是關於電腦視覺的,平時主要用Clion/PyCharm基於opencv-python/C++進行開發。近期閑來沒事體驗一下蘋果官方的開發工具,由於遇到了一 ...
  • Set介面 介紹 無序(添加和取出的順序不一致),沒有索引 不允許重覆,所以最多包含一個null JDK API中Set介面實現類有 Set介面常用方法 和List介面一樣,Set介面也是Collection的子介面,因此,常用方法和Collection介面一樣 特點 不能存放重覆的元素 set介面 ...
  • 主從複製 SLAVEOF 新舊複製功能 舊版複製功能 舊版複製功能的實現為 同步 和 命令傳播: 當剛連上Master時,要做一次全同步: sequenceDiagram participant Slave participant Master Slave->>Master: SYNC Master ...
  • 文章首發於公眾號:BiggerBoy 有讀者說面試被問到怎麼用SQL模擬資料庫死鎖? 這位讀者表示對Java中的死鎖還是略知一二的,但是突然用SQL寫死鎖的案例之前還真沒遇到過,這個問題沒答上來。所以今天就帶大家一起來看下怎麼用SQL讓資料庫中產生死鎖。 什麼是死鎖 說到死鎖,還是先來複習下什麼是死 ...
  • 本次要講的內容是利用RecyclerView顯示Users列表的信息,如下圖所示。 首先我們來創建模型User.java package com.example.mytest.User; import java.util.UUID; public class User{ private UUID m ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...