基於ArkUI框架開發-ImageKnife渲染層重構

OpenHarmony開發者發表於2023-04-06

基於ArkUI框架開發-ImageKnife渲染層重構ImageKnife是一款影像載入快取庫,主要功能特性如下:

●支援記憶體快取,使用LRUCache演算法,對圖片資料進行記憶體快取。
●支援磁碟快取,對於下載圖片會儲存一份至磁碟當中。
●支援進行圖片變換:支援影像畫素源圖片變換效果。
●支援使用者配置引數使用:(例如:配置是否開啟一級記憶體快取,配置磁碟快取策略,配置僅使用快取載入資料,配置圖片變換效果,配置佔點陣圖,配置載入失敗佔點陣圖等)。

更多細節請訪問原始碼倉庫地址:https://gitee.com/openharmony-tpc/ImageKnife

背景說明

早期ImageKnife三方庫在實現渲染部分的時候,使用的是image元件來展示圖片的。由於image元件其實是一個完整的集載入解析和圖片展示的元件,渲染的模式只能透過配置固定引數進行,面對複雜的需求場景,可能會出現擴充套件性不夠的情況。

現在隨著時間的推移渲染元件又多了一位重量級選手Canvas元件。可以透過2個元件渲染層的能力對比進行判斷渲染層最終交由哪個元件展示。

如果想了解更多ImageKnife的背景知識,可以點選連結檢視之前的文章介紹:

舊版本ImageKnife載入流程介紹https://developer.huawei.com/consumer/cn/forum/topic/02038645...

元件選型,能力對比

首先我們來看看Image元件和Canvas元件對於渲染這一塊的支援情況。

圖片

從上表我們可以看出:

Image元件雖然支援了PixelMap的繪製,但是基本沒有繪製控制能力,而且擴充套件效能力也比較弱,並且渲染過程不可見,也無法對繪製內容進行更多操作。

而Canvas元件屬於更加底層的渲染元件,可以完美地控制繪製內容,並且渲染過程可見,符合了開發者對於擴充套件性要求較高的定製場景。

重構前後能力對比

圖片

重構完成的內容

1.使用canvas元件替代Image元件進行渲染展示圖片。

2.所有影像資料在渲染層都轉換為PixelMap,方便統一管理和擴充套件。

3.所有回撥節點,統一抽象成介面,方便後續進行擴充套件,提高程式碼可維護性。

4.所有的回撥節點繪製的實現,都採用了責任鏈模式,提高了自定義繪製擴充套件能力。

5.將部分通用方法封裝成工廠方法,減少開發者程式碼量。

6.通用方法從配置引數剝離,可採用鏈式呼叫方式使用這些方法。

7.為了支援列表ImageKnifeOption引數使用@LinkObject修飾,同時ImageKnifeOption型別被@Observed修飾繼承,不可被繼承。

重構中比較重要的點

點1:回撥介面抽象為IDrawLifeCycle介面

渲染繪製是主執行緒才能操作。因此我們可以對渲染順序進行了梳理,大致流程:展示佔點陣圖->展示網路載入進度->展示縮圖->展示主圖->展示重試圖層->展示失敗佔點陣圖

圖片

這裡每個藍色的小方格都代表著一個資料返回的回撥介面,我們需要在這個回撥介面,處理接下來內容渲染的展示操作。因為每個回撥的流程是固定的,有點像生命週期的流程。所以我這邊抽象成介面IDrawLifeCycle繪製生命週期進行表達。這其實也是為了後面擴充套件做了準備。

點2:繪製實現採用責任鏈模式

我們支援了使用者配置自定義繪製和全域性配置自定義繪製的能力。採用了責任鏈模式實現,使用者引數設定->全域性引數設定->自定義元件內部設定。這樣設計的好處就是保留了使用者擴充套件的能力,使用者可以參與自定義繪製。
圖片

點3:提供了ImageKnifeDrawFactory工廠類

在開發者需要進行自定義繪製時,必須實現IDrawLifeCycle的6個介面。為了簡化開發者操作,這裡提供了ImageKnifeDrawFactory工廠類。

ImageKnifeDrawFactory裡面封裝了圓角、橢圓、百分比下載等實現,簡化使用者操作。當然更多的需求,可以參考該工廠類自行擴充套件實現。

這裡我們提供簡單的場景示例:

場景1:一句程式碼,加個圓角效果

程式碼如下:

import {ImageKnifeComponent} from '@ohos/imageknife'
import {ImageKnifeOption} from '@ohos/imageknife'
import {ImageKnifeDrawFactory} from '@ohos/imageknife'
@Entry
@Component
struct Index {
  @State imageKnifeOption1: ImageKnifeOption =
    { // 載入一張本地的png資源(必選)
      loadSrc: $r('app.media.pngSample'),
      // 主圖的展示模式是 縮放至適合元件大小,並且在元件底部繪製
      mainScaleType: ScaleType.FIT_END,
      // 佔點陣圖使用本地資源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%')
  }
}

圖片

場景2:全域性配置網路下載百分比效果展示

僅需一句程式碼所有網路圖片載入都能新增網路下載百分比效果展示。程式碼如下:

import AbilityStage from '@ohos.application.Ability'
import { ImageKnife,ImageKnifeDrawFactory} from '@ohos/imageknife'

export default class EntryAbility extends Ability {
    onCreate(want,launchParam) {
        globalThis.ImageKnife = ImageKnife.with(this.context);
        // 全域性配置網路載入進度條       
        globalThis.ImageKnife.setDefaultLifeCycle(ImageKnifeDrawFactory.createProgressLifeCycle("#10a5ff", 0.5))
    }
}

這裡大家可能會問,為什麼會將這個IDrawLifeCycle放在EntryAbility裡面實現?

這是因為網路下載百分比進度很多時候都是全域性通用,如果有需要全域性配置的自定義展示方案。推薦在EntryAbility裡面,往ImageKnife的setDefaultLifeCycle函式中注入,即可將ImageKnifeComponent中的預設繪製方案替換。

在這裡我們實現的效果如下圖所示。

圖片

點4:通用屬性方法和屬性已經支援鏈式呼叫

比如下面的程式碼的寬高已經不用設定在ImageKnifeOption物件中了,直接在自定義元件下方鏈式呼叫設定即可。

import {ImageKnifeComponent,ImageKnifeOption,ImageKnifeDrawFactory} from '@ohos/imageknife'
@Entry
@Component
struct Index {
  @State imageKnifeOption1: ImageKnifeOption =
    { // 載入一張本地的png資源(必選)
      loadSrc: $r('app.media.pngSample'),
    };
  build() {
    Scroll() {
      Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
        ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption1 })
          .width(300) // 自定義元件已支援設定通用屬性和事件,這裡寬高設定放在鏈式呼叫中完成
          .height(300)
      }
    }
    .width('100%')
    .height('100%')
  }
}

點5:如何在列表使用

支援列表使用圖片載入,只需要維護一個@State options:Array<ImageKnifeOption> = []物件即可

import {ImageKnifeOption,ImageKnifeComponent} from '@ohos/imageknife'
@Entry
@Component
struct BasicTestFeatureAbilityPage {
  urls=[
   "http://e.hiphotos.baidu.com/image/pic/item/a1ec08fa513d2697e542494057fbb2fb4316d81e.jpg",
   "http://c.hiphotos.baidu.com/image/pic/item/30adcbef76094b36de8a2fe5a1cc7cd98d109d99.jpg",
   "http://h.hiphotos.baidu.com/image/pic/item/7c1ed21b0ef41bd5f2c2a9e953da81cb39db3d1d.jpg",
   "http://g.hiphotos.baidu.com/image/pic/item/55e736d12f2eb938d5277fd5d0628535e5dd6f4a.jpg",
   "http://e.hiphotos.baidu.com/image/pic/item/4e4a20a4462309f7e41f5cfe760e0cf3d6cad6ee.jpg",
 ]
  @State options:Array<ImageKnifeOption> = []
  aboutToAppear(){
    this.options =  this.urls.map((url)=>{
      return {
        loadSrc:url
      }
    })
    console.log('this.options length ='+this.options.length)
  }
  build() {
    Stack({ alignContent: Alignment.TopStart }) {
      Column() {
        List({ space: 20, initialIndex: 0 }) {
          ForEach(this.options, (item) => {
            ListItem() {
              ImageKnifeComponent({imageKnifeOption:item}).width(300).height(300)
            }
          }, item => item.loadSrc)
        }
        .listDirection(Axis.Vertical) // 排列方向
        .divider({ strokeWidth: 2, color: 0xFFFFFF, startMargin: 20, endMargin: 20 }) // 每行之間的分界線
        .edgeEffect(EdgeEffect.None) // 滑動到邊緣無效果
        .chainAnimation(false) // 聯動特效關閉
      }.width('100%')
    }.width('100%').height('100%').backgroundColor(0xDCDCDC).padding({ top: 5 })
  }
}

渲染層重構的總結

綜上可知,此次重構渲染層,一共新增了6個基礎能力,適配了IDE最新版特性自定義元件可鏈式呼叫通用屬性和方法,並且採用適合的設計模式保留了自定義元件繪製部分的擴充能力。展示了部分常用場景下使用程式碼的方式,幫助開發者更快上手開發。

最後在OpenHarmony不斷推陳出新之際,三方庫ImageKnife也應該激流勇進,不斷地提升元件的實用性和適用性,為開發者創造一個良好的開發體驗。

我們將會持續更新ImageKnife三方庫,後續會切換成GPU來渲染圖片變換能力,不斷進行效能最佳化,提升ImageKnife三方庫。

同時也歡迎開發者使用和提issue。

圖片

相關文章