Development based on ArkUI framework - ImageKnife rendering layer reconstruction

ImageKnife is an image loading cache library, the main features are as follows:

●Support memory cache, use LRUCache algorithm to cache image data in memory.

●Supports disk caching, and will save a copy of the downloaded pictures to the disk.

●Support image transformation: support image pixel source image transformation effect.

●Supports the use of user configuration parameters: (for example: configure whether to enable the first-level memory cache, configure the disk cache strategy, configure only using the cache to load data, configure image transformation effects, configure placeholder images, configure placeholder images that fail to load, etc.).

For more details, please visit the source code warehouse address: OpenHarmony-TPC/ImageKnife

Background Note

When the early ImageKnife tripartite library implemented the rendering part, it used the image component to display pictures. Since the image component is actually a complete set of loading, parsing and image display components, the rendering mode can only be performed by configuring fixed parameters. In the face of complex demand scenarios, there may be insufficient scalability.

Now with the passage of time, the rendering component has added a heavyweight Canvas component. You can judge which component will render the rendering layer by comparing the capabilities of the rendering layers of the two components.

If you want to know more about the background knowledge of ImageKnife, you can click on the link to view the previous article introduction:

Introduction to the loading process of the old version of ImageKnife

Huawei Developer Forum

Component selection, capability comparison

First, let's take a look at the support of the Image component and the Canvas component for rendering.

From the above table we can see that:

Although the Image component supports the drawing of PixelMap, it basically has no drawing control ability, and its scalability is relatively weak, and the rendering process is invisible, and it cannot perform more operations on the drawn content.

The Canvas component is a lower-level rendering component, which can perfectly control the drawing content, and the rendering process is visible, which meets the custom scenarios where developers have high requirements for scalability.

Capability comparison before and after refactoring

What has been refactored

1. Use the canvas component instead of the Image component to render and display pictures.

2. All image data are converted to PixelMap in the rendering layer, which is convenient for unified management and expansion.

3. All callback nodes are uniformly abstracted into interfaces, which facilitates subsequent expansion and improves code maintainability.

4. All callback node drawing implementations adopt the chain of responsibility model, which improves the ability to expand custom drawing.

5. Encapsulate some common methods into factory methods to reduce the amount of code for developers.

6. Common methods are stripped from configuration parameters, and these methods can be used in chain calls.

7. In order to support list ImageKnifeOption parameters use @LinkObject decoration, and ImageKnifeOption type is inherited by @Observed decoration and cannot be inherited.

Important points in refactoring

Point 1: The callback interface is abstracted as IDrawLifeCycle interface

Rendering and drawing can only be done on the main thread. Therefore, we can sort out the rendering order, the general process: display placeholder image -> display network loading progress -> display thumbnail image -> display main image -> display re-layer layer -> display failed placeholder image

Each small blue square here represents a callback interface for data return. We need to process the display operation of the next content rendering in this callback interface. Because the flow of each callback is fixed, a bit like the flow of the life cycle. So I abstracted it into the interface IDrawLifeCycle to express the life cycle. This is actually a preparation for future expansion.

Point 2: The drawing implementation adopts the responsibility chain mode

We support the ability to configure custom drawing by user and configure custom drawing globally. Adopt the chain of responsibility mode to realize, user parameter setting->global parameter setting->custom component internal setting. The advantage of this design is that it retains the ability of users to expand, and users can participate in custom drawing.

Point 3: Provides the ImageKnifeDrawFactory factory class

When developers need to do custom drawing, they must implement the 6 interfaces of IDrawLifeCycle. In order to simplify the developer's operation, the ImageKnifeDrawFactory factory class is provided here.

ImageKnifeDrawFactory encapsulates the implementation of rounded corners, ellipses, and percentage downloads to simplify user operations. Of course, for more requirements, you can refer to the factory class to expand and implement it yourself.

Here we provide simple scenario examples:

Scenario 1: One line of code, add a rounded corner effect

code show as below:

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%')
  }
}

Scenario 2: Global configuration network download percentage effect display

Only one line of code is needed for all network image loading to add a new network download percentage effect display. code show as below:

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))
    }
}

Here you may ask, why is this IDrawLifeCycle implemented in EntryAbility?

This is because the network download percentage progress is often used globally, if there is a custom display scheme that requires global configuration. It is recommended to inject into the setDefaultLifeCycle function of ImageKnife in EntryAbility to replace the default drawing scheme in ImageKnifeComponent.

Here we achieve the effect shown in the figure below.

Point 4: Common property methods and properties already support chain calls

For example, the width and height of the following code no longer need to be set in the ImageKnifeOption object, and the settings can be directly chained under the custom component.

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%')
  }
}

Point 5: How to use in the list

The support list is loaded using images, only need to maintain a @State options:Array<ImageKnifeOption> = []

object

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 })
  }
}

Summary of rendering layer refactoring

To sum up, the reconstruction of the rendering layer this time has added a total of 6 basic capabilities, adapting to the latest version of the IDE. Custom components can chain-call general properties and methods, and adopt suitable design patterns to retain custom components. Extended ability to draw parts. Shows how to use code in some common scenarios to help developers get started faster.

Finally, when OpenHarmony continues to innovate, the three-party library ImageKnife should also advance bravely, continuously improve the practicability and applicability of components, and create a good development experience for developers.

We will continue to update the ImageKnife tripartite library, and then switch to the GPU to render image transformation capabilities, continue to optimize performance, and improve the ImageKnife tripartite library.

At the same time, developers are welcome to use and raise issues.

Guess you like

Origin blog.csdn.net/OpenHarmony_dev/article/details/129983480