关于harmonyos:ImageKnife组件让小白也能轻松搞定图片开发

265次阅读

共计 8625 个字符,预计需要花费 22 分钟才能阅读完成。

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

以上就是本期全部内容,祝贺大家花几分钟工夫播种了一个实用的组件。心愿宽广开发者能利用这个弱小的开源组件开发出更多精美的利用。

正文完
 0