目录

如何在卡片中刷新展示网络图片

问题现象

在卡片上展示网络图片时,由于卡片环境的限制,无法直接使用网络图片URL。需要先将网络图片下载到本地沙箱,然后通过Image组件显示。

常见的场景包括:

背景知识

解决方案

由于ArkTS卡片不支持直接使用网络相关路径,如果需要在卡片上展示网络图片,则需要先将网络图片下载至本地沙箱,然后Image组件通过入参(memory://fileName)中的(memory://)标识来进行远端内存图片显示。

下载网络图片需要使用网络能力,申请ohos.permission.INTERNET权限,配置方式为在module.json5配置文件的requestPermissions标签中声明权限:

"requestPermissions": [
  {
    "name": "ohos.permission.INTERNET"
  }
]

不同场景均需要通过httpRequest.request方法下载网络资源,在下载完成的回调中调用updateForm接口更新图片数据。

class FormDataClass {
  // 卡片需要显示图片场景,必须和下列字段formImages中的key相同
  imgName: string = '';
  // 卡片需要显示图片场景,必填字段(formImages不可缺省或改名),fileName对应fd
  formImages: Record<string, number> = {};
}

// 下载文件
download(context:
Context, formId:
string
)
{
  let netFile = 'https://XXXX'; // 网络图片地址
  let httpRequest = http.createHttp()
  let tempDir = context.getApplicationContext().tempDir;
  let fileName = 'file' + Date.now();
  let tmpFile = tempDir + '/' + fileName;
  let imgFile = undefined
  let imgMap: Record<string, number> = {};

  // 下载网络资源
  httpRequest.request(netFile).then((data) => {
    if (data?.responseCode === http.ResponseCode.OK) {
      try {
        let imgFile = fileIo.openSync(tmpFile, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
        imgMap[fileName] = imgFile.fd;
        try {
          fileIo.write(imgFile.fd, data.result as ArrayBuffer).then((writeLen) => {
            console.info('write data to file succeed and size is:' + writeLen);
            try {
              let formData: FormDataClass = {
                imgName: fileName,
                formImages: imgMap
              }
              const formInfo = formBindingData.createFormBindingData(formData)
              formProvider.updateForm(formId, formInfo).then(() => {
                console.info('FormAbility updateForm success.');
              }).catch((e: BusinessError) => {
                console.error(`FormAbility updateForm failed: 63 ${JSON.stringify(e)}`);
              });
            } catch (error) {
              console.error(`FormAbility updateForm failed: ${JSON.stringify(error)}`);
            }
          })
        } catch (err) {
          console.error('write data to file failed with error message: ' + err.message + ', error code: ' + err.code);
        }
      } catch (e) {
        console.error(`openSync failed: ${JSON.stringify(e as BusinessError)}`);
      } finally {
        // 在fileIo.closeSync执行之前,确保formProvider.updateForm已执行完毕。
        fileIo.closeSync(imgFile);
      }
    } else {
      console.error(`ArkTSCard download task failed`);
      let param: Record<string, string> = {
        'text': '刷新失败'
      };
      const formInfo = (formBindingData.createFormBindingData(param))
      formProvider.updateForm(formId, formInfo);
    }
    httpRequest.destroy();
  })
}

在WidgetCard卡片页面中使用LocalStorageProp装饰需要刷新的卡片数据,使用Image组件加载远端内存图片。

@Entry
@Component
struct WidgetCard {
  @LocalStorageProp('imgName') imgName: string = '';

  build() {
    Column() {
      Image('memory://' + this.imgName)
        .width(100)
    }
    .width('100%')
    .height('100%')
  }
}

不同场景的区别在于下载网络图片的时机,具体区分为以下四种场景。

常见FAQ

Q:下载网络图片至沙箱,卡片加载图片失败可能是什么原因导致?

A:卡片中Image组件通过入参(memory://fileName)中的(memory://)标识来进行远端内存图片显示。网络资源在下载过程中,仍可通过openSync打开并获取文件fd。因此,显示卡片时需要确保网络图片已经在沙箱下载完成,避免网络图片下载过程中将fd传递至卡片,造成卡片图片加载失败。

Q:为什么修改传递给卡片formImages中imgName对应的文件fd,卡片中的图片没有更新?

A:Image组件通过传入的参数是否有变化来决定是否刷新图片,因此EntryFormAbility每次传递过来的imgName都需要不同,连续传递两个相同的imgName时,图片不会刷新。

Q:在卡片上展示的图片有什么限制?

A:在卡片刷新过程中,卡片UI通过@LocalStorageProp接收卡片数据时,FormBindingData对象会序列化,即卡片数据会转换成string类型。从API version 20开始,图片文件数量上限为20张,所有图片总内存大小不超过10MB,超出限制的图片会显示异常,API version 19及之前的版本,图片文件数量上限为5张,每张限制内存2MB。

总结

场景

描述

注意点

创建卡片时直接展示网络图片

桌面创建卡片时,直接加载网络图片。

创建卡片时触发onAddForm生命周期,但是由于下载网络图片存在延迟,因此需要先返回默认数据。

主应用中主动刷新卡片中网络图片

主应用下载网络图片,同步至卡片。

应用内获取已上桌卡片可通过首选项或者formProvider.getPublishedRunningFormInfos接口,更新时需要过滤具体的卡片。

卡片主动刷新网络图片

已上桌卡片自动触发更新。

桌面卡片通过发送message事件即可触发FormExtensionAbility的onFormEvent生命周期回调。

定时/定点被动刷新卡片中网络图片

已上桌卡片定时/定点被动刷新。

定时/定点刷新需要在form_config.json中进行配置,且卡片定时/定点刷新频率和优先级存在约束与限制。