diff --git a/CHANGELOG.md b/CHANGELOG.md index a90036d..cfa46cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,11 @@ ## 3.0.0-rc.1 -- 新增从内存或文件缓存获取图片数据接口isUrlExist +- 新增从内存或文件缓存获取图片数据接口getCacheImage - 新增图片预加载preLoadCache并会犯文件缓存路径 -- ImageKnifeOption新增writeCacheStrategy存入策略 +- ImageKnifeOption新增writeCacheStrategy存入策略(只存入内存或文件缓存) - ImageKnifeOption新增onlyRetrieveFromCache仅用缓存加载 - 新增单个和全局请求头 - 补齐自定key特性 +- 获取组件宽高改用onSizeChange ## 3.0.0-rc.0 - 使用Image组件替换Canvas组件渲染,并重构大部分的实现逻辑,提升渲染性能 diff --git a/README.md b/README.md index 28d5367..69c998a 100644 --- a/README.md +++ b/README.md @@ -124,29 +124,30 @@ ImageKnifeComponent({ ImageKnifeOption: ## 接口说明 ### ImageKnifeOption参数列表 -| 参数名称 | 入参内容 | 功能简介 | -|-----------------------|--------------------------------|---------------| -| loadSrc | string、PixelMap、Resource | 主图展示 | -| placeholderSrc | PixelMap、Resource | 占位图图展示(可选) | -| errorholderSrc | PixelMap、Resource | 错误图展示(可选) | -| objectFit | ImageFit | 图片展示样式(可选) | -| writeCacheStrategy | WriteCacheStrategy_Type | 写入缓存策略(可选) | -| onlyRetrieveFromCache | boolean | 仅使用缓存加载数据(可选) | +| 参数名称 | 入参内容 | 功能简介 | +|-----------------------|-------------------------------|---------------| +| loadSrc | string、PixelMap、Resource | 主图展示 | +| placeholderSrc | PixelMap、Resource | 占位图图展示(可选) | +| errorholderSrc | PixelMap、Resource | 错误图展示(可选) | +| objectFit | ImageFit | 图片展示样式(可选) | +| writeCacheStrategy | WriteCacheStrategyType | 写入缓存策略(可选) | +| onlyRetrieveFromCache | boolean | 跳过网络和本地请求(可选) | | customGetImage | (context: Context, src: string | 自定义网络(可选) | | Resource | 错误占位图数据源 | -| border | BorderOptions | 边框圆角(可选) | -| priority | taskpool.Priority | 加载优先级(可选) | -| context | common.UIAbilityContext | 上下文(可选) | -| progressListener | (progress: number)=>void | 进度(可选) | +| border | BorderOptions | 边框圆角(可选) | +| priority | taskpool.Priority | 加载优先级(可选) | +| context | common.UIAbilityContext | 上下文(可选) | +| progressListener | (progress: number)=>void | 进度(可选) | | signature | ObjectKey | 自定义缓存关键字 | -| headerOption | Array | 设置请求头 | +| headerOption | Array | 设置请求头 | ### ImageKnife接口 -| 参数名称 | 入参内容 | 功能简介 | -|--------------|---------------------------|-------------| -| preLoadCache | url:string | 预加载并返回文件缓存路径 | -| isUrlExist | url: string, cacheType: Cache_Type | 从内存或文件缓存中获取资源 | -| addHeader | key: string, value: Object | 全局设置请求头 | -| deleteHeader | key: string | 全局删除请求头 | +| 参数名称 | 入参内容 | 功能简介 | +|--------------|---------------------------|---------------| +| preLoadCache | url:string | 预加载并返回文件缓存路径 | +| getCacheImage | url: string, cacheType: CacheType | 从内存或文件缓存中获取资源 | +| addHeader | key: string, value: Object | 全局添加请求头属性 | +| serHeaderOptions | Array | 全局设置请求头 | +| deleteHeader | key: string | 全局删除请求头 | | setEngineKeyImpl | CustomEngineKeyImpl | 全局配置缓存key | ## 约束与限制 diff --git a/entry/src/main/ets/pages/TestIsUrlExist.ets b/entry/src/main/ets/pages/TestIsUrlExist.ets index 5414fbe..90de4e9 100644 --- a/entry/src/main/ets/pages/TestIsUrlExist.ets +++ b/entry/src/main/ets/pages/TestIsUrlExist.ets @@ -12,7 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ImageKnifeComponent,ImageKnife,ImageKnifeOption,ImageKnifeData,Cache_Type } from '@ohos/imageknife' +import { ImageKnifeComponent,ImageKnife,ImageKnifeOption,ReadCacheStrategyType } from '@ohos/imageknife' @Entry @Component @@ -31,14 +31,14 @@ struct TestIsUrlExist { }) Button("内存缓存获取gif").onClick(()=>{ ImageKnife.getInstance() - .getCacheImage("https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658",Cache_Type.MemoryCache) + .getCacheImage("https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658",ReadCacheStrategyType.Memory) .then((data)=>{ this.source = data!.source }) }) Button("文件缓存获取gif").onClick(()=>{ ImageKnife.getInstance() - .getCacheImage("https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658",Cache_Type.FileCache) + .getCacheImage("https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658",ReadCacheStrategyType.File) .then((data)=>{ this.source1 = data!.source }) @@ -50,14 +50,14 @@ struct TestIsUrlExist { }) Button("内存缓存获取").onClick(()=>{ ImageKnife.getInstance() - .getCacheImage('https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp',Cache_Type.MemoryCache) + .getCacheImage('https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp',Cache_Type.Memory) .then((data)=>{ this.source = data!.source }) }) Button("文件缓存获取").onClick(()=>{ ImageKnife.getInstance() - .getCacheImage('https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp',Cache_Type.FileCache) + .getCacheImage('https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp',Cache_Type.File) .then((data)=>{ this.source1 = data!.source }) diff --git a/library/index.ets b/library/index.ets index 438326c..488697f 100644 --- a/library/index.ets +++ b/library/index.ets @@ -14,7 +14,7 @@ export { IEngineKey } from './src/main/ets/key/IEngineKey' export { ObjectKey } from './src/main/ets/model/ObjectKey' -export { ImageKnifeData , Cache_Type} from "./src/main/ets/model/ImageKnifeData" +export { ImageKnifeData , ReadCacheStrategyType} from "./src/main/ets/model/ImageKnifeData" diff --git a/library/oh-package.json5 b/library/oh-package.json5 index 0b53565..2baf06a 100644 --- a/library/oh-package.json5 +++ b/library/oh-package.json5 @@ -14,7 +14,7 @@ "main": "index.ets", "repository": "https://gitee.com/openharmony-tpc/ImageKnife", "type": "module", - "version": "3.0.0-rc.0", + "version": "3.0.0-rc.1", "dependencies": { }, diff --git a/library/src/main/ets/ImageKnife.ets b/library/src/main/ets/ImageKnife.ets index c0ce535..362cde5 100644 --- a/library/src/main/ets/ImageKnife.ets +++ b/library/src/main/ets/ImageKnife.ets @@ -13,13 +13,13 @@ * limitations under the License. */ import { ImageKnifeRequest } from './ImageKnifeRequest'; -import { Cache_Type, ImageKnifeData } from './model/ImageKnifeData'; +import { ReadCacheStrategyType, ImageKnifeData } from './model/ImageKnifeData'; import { MemoryLruCache } from './utils/MemoryLruCache'; import { IMemoryCache } from './utils/IMemoryCache' import { FileCache } from './utils/FileCache'; import { ImageKnifeDispatcher } from './ImageKnifeDispatcher'; import { IEngineKey } from './key/IEngineKey'; -import { ImageKnifeOption } from './ImageKnifeOption'; +import { HeaderOptions, ImageKnifeOption } from './ImageKnifeOption'; import { DefaultEngineKey } from './key/DefaultEngineKey'; import { FileTypeUtil } from './utils/FileTypeUtil'; import { util } from '@kit.ArkTS'; @@ -56,11 +56,27 @@ export class ImageKnife { this.fileCache = new FileCache(context, size, memory) this.fileCache.initFileCache() } - //全局设置请求头调用方法 + /** + * 全局添加单个请求头 + * @param key 请求头属性 + * @param value 请求头值 + */ addHeader(key: string, value: Object) { this.headerMap.set(key, value); } - + /** + * 全局设置请求头调用方法 + * @param options 请求头数组 + */ + serHeaderOptions(options:Array) { + options.forEach((value)=>{ + this.headerMap.set(value.key,value.value) + }) + } + /** + * 删除单个请求头属性 + * @param key 请求头属性 + */ deleteHeader(key: string) { this.headerMap.delete(key); } @@ -103,13 +119,17 @@ export class ImageKnife { getFileCache(): FileCache{ return this.fileCache as FileCache } - // 预加载到文件缓存,并返回缓存路径 - preLoadCache(url:string): Promise { + /** + * 预加载到缓存 + * @param loadSrc 图片地址url + * @returns 返回文件缓存路径 + */ + preLoadCache(loadSrc:string): Promise { return new Promise((resolve,reject)=>{ let imageKnifeOption = new ImageKnifeOption() - imageKnifeOption.loadSrc = url + imageKnifeOption.loadSrc = loadSrc let engineKeyImpl: IEngineKey = new DefaultEngineKey() - let keys = engineKeyImpl.generateCacheKey(url) + let keys = engineKeyImpl.generateCacheKey(loadSrc) let cachePath = ImageKnife.getInstance().getFileCache().getFileToPath(keys) if(cachePath == null || cachePath == "" || cachePath == undefined) { let request = new ImageKnifeRequest( @@ -120,12 +140,7 @@ export class ImageKnife { 0, { showPixelMap(version: number, pixelMap: PixelMap | string) { - let cachePaths = ImageKnife.getInstance().getFileCache().getFileToPath(keys) - if(cachePaths != "") { - resolve(cachePaths) - } else { - reject(undefined) - } + resolve(ImageKnife.getInstance().getFileCache().getFileToPath(keys)) } } ) @@ -135,52 +150,65 @@ export class ImageKnife { } }) } - // 从内存或文件缓存中获取资源 - getCacheImage(url: string, cacheType: Cache_Type):Promise { + /** + * 从内存或文件缓存中获取图片数据 + * @param url 图片地址url + * @param cacheType 缓存策略 + * @returns 图片数据 + */ + getCacheImage(loadSrc: string, cacheType: ReadCacheStrategyType = ReadCacheStrategyType.Default):Promise { return new Promise((resolve,reject)=>{ let engineKeyImpl: IEngineKey = new DefaultEngineKey() - if(cacheType == Cache_Type.MemoryCache) { - let keys = engineKeyImpl.generateCacheKey(url) - let memoryCache:ImageKnifeData | undefined = ImageKnife.getInstance() - .loadFromMemoryCache(keys) - resolve(memoryCache) - } else if (cacheType == Cache_Type.FileCache) { - let keys = engineKeyImpl.generateCacheKey(url) - let buffer = ImageKnife.getInstance().loadFromFileCache(keys) - if(buffer != undefined) { - let fileTypeUtil = new FileTypeUtil(); - let typeValue = fileTypeUtil.getFileType(buffer); - if (typeValue === 'gif' || typeValue === 'webp') { - let base64Help = new util.Base64Helper() - - let base64str = "data:image/" + typeValue + ";base64," + base64Help.encodeToStringSync(new Uint8Array(buffer)) - resolve({ - source:base64str, - imageWidth: 0, - imageHeight: 0 - }) - } - - let imageSource: image.ImageSource = image.createImageSource(buffer); - let decodingOptions: image.DecodingOptions = { - editable: true, - } - - imageSource.createPixelMap(decodingOptions) - .then((pixelmap: PixelMap) => { - resolve({ - source:pixelmap, - imageWidth: 0, - imageHeight: 0 - }) - imageSource.release() - }) - } else { - resolve(undefined) - } + if(cacheType == ReadCacheStrategyType.Memory) { + resolve(this.ReadMemoryCache(loadSrc,engineKeyImpl)) + } else if (cacheType == ReadCacheStrategyType.File) { + this.ReadFileCache(loadSrc,engineKeyImpl,resolve) + } else { + let data = this.ReadMemoryCache(loadSrc,engineKeyImpl) + data == undefined ? this.ReadFileCache(loadSrc,engineKeyImpl,resolve) : data } }) } + ReadMemoryCache(loadSrc:string,engineKey:IEngineKey): ImageKnifeData | undefined{ + let keys = engineKey.generateCacheKey(loadSrc) + return ImageKnife.getInstance() + .loadFromMemoryCache(keys) + } + ReadFileCache(loadSrc:string,engineKey:IEngineKey,onComplete:(data:ImageKnifeData | undefined)=>void){ + let keys = engineKey.generateCacheKey(loadSrc) + let buffer = ImageKnife.getInstance().loadFromFileCache(keys) + if(buffer != undefined) { + let fileTypeUtil = new FileTypeUtil(); + let typeValue = fileTypeUtil.getFileType(buffer); + if (typeValue === 'gif' || typeValue === 'webp') { + let base64Help = new util.Base64Helper() + + let base64str = "data:image/" + typeValue + ";base64," + base64Help.encodeToStringSync(new Uint8Array(buffer)) + onComplete({ + source:base64str, + imageWidth: 0, + imageHeight: 0 + }) + } + + let imageSource: image.ImageSource = image.createImageSource(buffer); + let decodingOptions: image.DecodingOptions = { + editable: true, + } + + imageSource.createPixelMap(decodingOptions) + .then((pixelmap: PixelMap) => { + onComplete({ + source:pixelmap, + imageWidth: 0, + imageHeight: 0 + }) + imageSource.release() + }) + } else { + onComplete(undefined) + } + } /** * 清除所有文件缓存 * @returns diff --git a/library/src/main/ets/ImageKnifeDispatcher.ets b/library/src/main/ets/ImageKnifeDispatcher.ets index 58cd662..aeaa2de 100644 --- a/library/src/main/ets/ImageKnifeDispatcher.ets +++ b/library/src/main/ets/ImageKnifeDispatcher.ets @@ -23,7 +23,7 @@ import common from '@ohos.app.ability.common'; import { FileCache } from './utils/FileCache'; import fs from '@ohos.file.fs'; import { ImageKnife } from './ImageKnife'; -import { ImageKnifeData, WriteCacheStrategy_Type } from './model/ImageKnifeData'; +import { ImageKnifeData, WriteCacheStrategyType } from './model/ImageKnifeData'; import http from '@ohos.net.http'; import image from '@ohos.multimedia.image'; import emitter from '@ohos.events.emitter'; @@ -147,7 +147,7 @@ export class ImageKnifeDispatcher { } } // 保存文件缓存 - if (requestJobResult.bufferSize > 0) { + if (requestJobResult.bufferSize > 0 && currentRequest.ImageKnifeOption.writeCacheStrategy !== WriteCacheStrategyType.Memory) { ImageKnife.getInstance().saveWithoutWriteFile(key, requestJobResult.bufferSize) } @@ -158,7 +158,7 @@ export class ImageKnifeDispatcher { } // 保存内存缓存 - if(currentRequest.ImageKnifeOption.writeCacheStrategy !== WriteCacheStrategy_Type.OnlyFile) { + if(currentRequest.ImageKnifeOption.writeCacheStrategy !== WriteCacheStrategyType.File) { ImageKnife.getInstance() .saveMemoryCache(this.engineKeyImpl.generateCacheKey(imageSrc, currentRequest.ImageKnifeOption.signature), ImageKnifeData) diff --git a/library/src/main/ets/ImageKnifeOption.ets b/library/src/main/ets/ImageKnifeOption.ets index 967ce65..21c4185 100644 --- a/library/src/main/ets/ImageKnifeOption.ets +++ b/library/src/main/ets/ImageKnifeOption.ets @@ -15,7 +15,7 @@ import taskpool from '@ohos.taskpool'; import common from '@ohos.app.ability.common' import { ObjectKey } from './model/ObjectKey'; -import { WriteCacheStrategy_Type } from './model/ImageKnifeData'; +import { WriteCacheStrategyType } from './model/ImageKnifeData'; export interface HeaderOptions { key: string; value: Object; @@ -40,7 +40,7 @@ export class ImageKnifeOption { border?: BorderOptions // 缓存策略 - writeCacheStrategy?: WriteCacheStrategy_Type + writeCacheStrategy?: WriteCacheStrategyType // 仅使用缓存加载数据 onlyRetrieveFromCache?: boolean = false; diff --git a/library/src/main/ets/components/ImageKnifeComponent.ets b/library/src/main/ets/components/ImageKnifeComponent.ets index 3b26200..5c17595 100644 --- a/library/src/main/ets/components/ImageKnifeComponent.ets +++ b/library/src/main/ets/components/ImageKnifeComponent.ets @@ -35,10 +35,6 @@ export struct ImageKnifeComponent { private currentHeight: number = 0 private componentVersion: number = 0 private currentContext: common.UIAbilityContext | undefined = undefined - @State keyCanvas: KeyCanvas = { - keyId: util.generateRandomUUID() - } - private listener: inspector.ComponentObserver = inspector.createComponentObserver(this.keyCanvas.keyId) aboutToAppear(): void { //闪动问题失效,注释相应代码后续修复 @@ -50,7 +46,6 @@ export struct ImageKnifeComponent { // this.pixelMap = memoryCache.source; // }else { LogUtil.log("aboutToAppear onLayoutComplete") - this.listener.on("layout", this.onLayoutComplete) // } } @@ -60,21 +55,36 @@ export struct ImageKnifeComponent { this.request.requestState = ImageKnifeRequestState.DESTROY this.request = undefined } - this.listener.off("layout", this.onLayoutComplete) } - aboutToRecycle(){ + + aboutToRecycle() { if (this.request !== undefined) { this.request.requestState = ImageKnifeRequestState.DESTROY this.request = undefined } } + build() { Image(this.pixelMap) .objectFit(this.ImageKnifeOption.objectFit === undefined ? ImageFit.Contain : this.ImageKnifeOption.objectFit) .width(this.adaptiveWidth) .height(this.adaptiveHeight) - .key(this.keyCanvas.keyId) .border(this.ImageKnifeOption.border) + .onSizeChange((oldValue:SizeOptions, newValue:SizeOptions) => { + this.currentWidth = newValue.width as number + this.currentHeight = newValue.height as number + this.lastWidth = oldValue.width as number + this.lastHeight = oldValue.height as number + if (this.currentWidth <= 0 || this.currentHeight <= 0) { + // 存在宽或者高为0,此次重回无意义,无需进行request请求 + } else { + // 前提:宽高值均有效,值>0. 条件1:当前宽高与上一次宽高不同 条件2:当前是第一次绘制 + if (this.currentHeight != this.lastHeight || this.currentWidth != this.lastWidth) { + LogUtil.log("execute request:width=" + this.currentWidth + " height= " + this.currentHeight) + ImageKnife.getInstance().execute(this.getRequest(this.currentWidth, this.currentHeight)) + } + } + }) } watchImageKnifeOption() { @@ -85,12 +95,14 @@ export struct ImageKnifeComponent { this.componentVersion++ ImageKnife.getInstance().execute(this.getRequest(this.currentWidth, this.currentHeight)) } - getCurrentContext(): common.UIAbilityContext{ - if(this.currentContext == undefined) { + + getCurrentContext(): common.UIAbilityContext { + if (this.currentContext == undefined) { this.currentContext = getContext(this) as common.UIAbilityContext } return this.currentContext } + getRequest(width: number, height: number): ImageKnifeRequest { if (this.request == undefined) { this.request = new ImageKnifeRequest( @@ -100,7 +112,7 @@ export struct ImageKnifeComponent { height, this.componentVersion, { - showPixelMap: async (version: number, pixelMap: PixelMap | string) => { + showPixelMap: async (version: number, pixelMap: PixelMap | string) => { if (version !== this.componentVersion) { return //针对reuse场景,不显示历史图片 } @@ -130,23 +142,6 @@ export struct ImageKnifeComponent { return this.request } - - onLayoutComplete: () => void = (): void => { - let value: componentUtils.ComponentInfo = componentUtils.getRectangleById(this.keyCanvas.keyId); - this.currentWidth = px2vp(value.size.width) - this.currentHeight = px2vp(value.size.height) - if (this.currentWidth <= 0 || this.currentHeight <= 0) { - // 存在宽或者高为0,此次重回无意义,无需进行request请求 - } else { - // 前提:宽高值均有效,值>0. 条件1:当前宽高与上一次宽高不同 条件2:当前是第一次绘制 - if (this.currentHeight != this.lastHeight || this.currentWidth != this.lastWidth) { - this.lastWidth = this.currentWidth - this.lastHeight = this.currentHeight - LogUtil.log("execute request:width=" + this.currentWidth + " height= " + this.currentHeight) - ImageKnife.getInstance().execute(this.getRequest(this.currentWidth, this.currentHeight)) - } - } - } } interface KeyCanvas { diff --git a/library/src/main/ets/model/ImageKnifeData.ets b/library/src/main/ets/model/ImageKnifeData.ets index 0cfd7e0..3ac9632 100644 --- a/library/src/main/ets/model/ImageKnifeData.ets +++ b/library/src/main/ets/model/ImageKnifeData.ets @@ -17,14 +17,26 @@ export interface ImageKnifeData { imageWidth: number, imageHeight: number } -export enum Cache_Type { - // 内存缓存 - MemoryCache = 0, - // 文件缓存 - FileCache = 1 +/** + * 读取缓存策略 + */ +export enum ReadCacheStrategyType { + // 默认-读取内存和文件缓存 + Default = 0, + // 只读取内存缓存 + Memory = 1, + // 只读取文件缓存 + File = 2 } -export enum WriteCacheStrategy_Type { - DEFAULT = 0,// 默认都开启 - OnlyFile = 1 // 仅缓存文件 +/** + * 写入缓存策略 + */ +export enum WriteCacheStrategyType { + // 默认-写入内存和文件缓存 + Default = 0, + // 只写入内存缓存 + Memory = 1, + // 只写入文件缓存 + File = 2 }