乐趣区

关于框架:基于ArkUI框架开发ImageKnife渲染层重构

基于 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。

退出移动版