HarmonyOS鸿蒙Next中如何使用卡片能力去切换图片

HarmonyOS鸿蒙Next中如何使用卡片能力去切换图片

如何使用卡片能力去切换图片

3 回复

开发者您好,可查看如下方案,实现在卡片中切换展示网络图片:

【解决方案】

由于ArkTS卡片不支持直接使用网络相关路径,如果需要在卡片上展示网络图片,则需要先将网络图片下载至本地沙箱,然后Image组件通过入参(memory://fileName)中的(memory://)标识来进行远端内存图片显示。下载网络图片需要使用网络能力,申请ohos.permission.INTERNET权限,配置方式为在module.json5配置文件的requestPermissions标签中声明权限。

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

不同场景的区别在于下载网络图片的时机。具体来说:

场景一:主应用内主动刷新切换卡片中网络图片。

已上桌的卡片,在主应用内可通过formProvider.getPublishedRunningFormInfos接口获取所有已加桌的卡片信息。接着,通过卡片名称可以过滤出需要更新的目标卡片信息。最后下载网络图片到本地,通过updateForm接口更新卡片的图片。示例代码为:

import { formBindingData, formInfo, formProvider } from '[@kit](/user/kit).FormKit';
import { common } from '[@kit](/user/kit).AbilityKit';
import { http } from '[@kit](/user/kit).NetworkKit';
import { fileIo } from '[@kit](/user/kit).CoreFileKit';
import { BusinessError } from '[@kit](/user/kit).BasicServicesKit';

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

@Component
@Entry
struct Index {
  // 下载文件
  download(context: Context, formId: string) {
    let netFile = 'XXX'; // 网络图片地址
    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();
    });
  }

  build() {
    Column() {
      Button('更新桌面网络图片').onClick(async () => {
        const context = this.getUIContext().getHostContext() as common.UIAbilityContext;
        // 获取上已上桌的卡片
        try {
          formProvider.getPublishedRunningFormInfos().then((data: formInfo.RunningFormInfo[]) => {
            console.info(`formProvider getPublishedRunningFormInfos, data: ${JSON.stringify(data)}`);

            data.forEach((form) => {
              let formId = form.formId;
              // 可根据名称作为条件进行过滤
              this.download(context, formId);
            });
          }).catch((error: BusinessError) => {
            console.error(`promise error, code: ${error.code}, message: ${error.message})`);
          });
        } catch (error) {
          console.error(`catch error, code: ${(error as BusinessError).code}, message: ${(error as BusinessError).message})`);
        }
      });
    }
    .width('100%')
    .height('100%');
  }
}

在WidgetCard卡片页面中使用LocalStorageProp装饰需要刷新切换的卡片数据,使用Image组件加载远端内存图片。示例代码为:

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

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

场景二:卡片主动刷新切换网络图片。

已上桌的卡片,在卡片可以通过postCardAction接口触发message事件拉起FormExtensionAbility,通过onFormEvent接口回调通知,以完成点击卡片控件后刷新卡片中图片的功能。值得注意的是,postCardAction操作需要配置卡片为动态卡片。详细步骤为:

  1. 在卡片页面通过注册Button的onClick点击事件回调,并在回调中调用postCardAction接口触发message事件。
@Entry
@Component
struct WidgetCard {
  @LocalStorageProp('imgName') imgName: string = '';

  build() {
    Column() {
      Button('更新图片').onClick(() => {
        postCardAction(this, {
          action: 'message',
          params: { msgTest: 'messageEvent' }
        });
      });

      Image('memory://' + this.imgName)
        .width(100);
    }
    .width('100%')
    .height('100%');
  }
}
  1. 在FormExtensionAbility的onFormEvent生命周期中下载网络资源。
import { formBindingData, FormExtensionAbility, formInfo, formProvider } from '[@kit](/user/kit).FormKit';
import { Want } from '[@kit](/user/kit).AbilityKit';
import { fileIo } from '[@kit](/user/kit).CoreFileKit';
import { http } from '[@kit](/user/kit).NetworkKit';
import { BusinessError } from '[@kit](/user/kit).BasicServicesKit';

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

export default class EntryFormAbility extends FormExtensionAbility {
  // 下载文件
  download(context: Context, formId: string) {
    let netFile = 'XXX'; // 网络图片地址
    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();
    });
  }

  onAddForm(want: Want) {
    // Called to return a FormBindingData object.
    let formId = want?.parameters?.[formInfo.FormParam.IDENTITY_KEY] as string;
    console.info('onAddForm', formId);
    const formData = '';
    return formBindingData.createFormBindingData(formData);
  }

  onCastToNormalForm(formId: string) {
    // Called when the form provider is notified that a temporary form is successfully
    // converted to a normal form.
    console.info('onCastToNormalForm', formId);
  }

  onUpdateForm(formId: string) {
    // Called to notify the form provider to update a specified form.
    console.info('onUpdateForm', formId);
  }

  onFormEvent(formId: string, message: string) {
    // Called when a specified message event defined by the form provider is triggered.
    console.info('onFormEvent', formId, message);
    this.download(this.context, formId);
  }

  onRemoveForm(formId: string) {
    // Called to notify the form provider that a specified form has been destroyed.
    console.info('onRemoveForm', formId);
  }

  onAcquireFormState(want: Want) {
    // Called to return a {@link FormState} object.
    console.info('onAcquireFormState', want.bundleName);
    return formInfo.FormState.READY;
  }
};

场景三:定时/定点被动刷新切换网络图片。

已上桌的卡片,可以在form_config.json配置文件的updateDuration字段中配置定时刷新间隔,也可以配置scheduledUpdateTime字段设置卡片定点刷新时间。在触发定点刷新后,系统会调用FormExtensionAbility的onUpdateForm生命周期回调,在回调中,通过updateForm接口更新卡片的图片。以卡片定点刷新为例,详细步骤为:

  1. 在form_config.json配置文件设置scheduledUpdateTime卡片定点刷新时间。

  2. 在FormExtensionAbility的onUpdateForm生命周期中下载网络资源。

import { formBindingData, FormExtensionAbility, formInfo, formProvider } from '[@kit](/user/kit).FormKit';
import { Want } from '[@kit](/user/kit).AbilityKit';
import { fileIo } from '[@kit](/user/kit).CoreFileKit';
import { http } from '[@kit](/user/kit).NetworkKit';
import { BusinessError } from '[@kit](/user/kit).BasicServicesKit';

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

export default class EntryFormAbility extends FormExtensionAbility {
  // 下载文件
  download(context: Context, formId: string) {
    let netFile = 'XXX'; // 网络图片地址
    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();
    });
  }

  onAddForm(want: Want) {
    // Called to return a FormBindingData object.
    let formId = want?.parameters?.[formInfo.FormParam.IDENTITY_KEY] as string;
    console.info('onAddForm', formId);
    const formData = '';
    return formBindingData.createFormBindingData(formData);
  }

  onCastToNormalForm(formId: string) {
    // Called when the form provider is notified that a temporary form is successfully
    // converted to a normal form.
    console.info('onCastToNormalForm', formId);
  }

  onUpdateForm(formId: string) {
    // Called to notify the form provider to update a specified form.
    console.info('onUpdateForm', formId);
    this.download(this.context, formId);
  }

  onFormEvent(formId: string, message: string) {
    // Called when a specified message event defined by the form provider is triggered.
    console.info('onFormEvent', formId, message);
  }

  onRemoveForm(formId: string) {
    // Called to notify the form provider that a specified form has been destroyed.
    console.info('onRemoveForm', formId);
  }

  onAcquireFormState(want: Want) {
    // Called to return a {@link Form

更多关于HarmonyOS鸿蒙Next中如何使用卡片能力去切换图片的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS Next中,使用卡片切换图片主要依赖ArkUI的声明式UI和状态管理。在卡片的build函数内,使用Image组件并绑定其src属性到一个状态变量(例如使用@State装饰的变量)。通过改变该状态变量的值(如点击按钮触发事件回调),即可动态更新卡片上显示的图片。图片资源需预先放入resources目录中引用,或使用网络图片URL。

在HarmonyOS Next中,使用卡片能力切换图片主要依赖于ArkUI的声明式开发范式与状态管理。核心是通过@State装饰器管理图片资源状态,并在卡片布局中绑定点击等事件来更新该状态。

关键步骤如下:

  1. 定义状态变量:使用@State装饰器定义一个变量来存储当前显示的图片资源ID(Resource类型)。例如:

    @State currentImage: Resource = $r('app.media.image1')
    
  2. 构建卡片UI:在自定义卡片的build函数中,使用Image组件显示状态变量currentImage。同时,可以添加一个按钮(如Button)或为Image组件本身设置点击事件(onClick)来触发切换。

    build() {
      Column() {
        // 显示当前图片
        Image(this.currentImage)
          .width('100%')
          .height(200)
        // 切换按钮
        Button('切换图片')
          .onClick(() => {
            this.changeImage()
          })
      }
    }
    
  3. 实现切换逻辑:在点击事件的处理函数(例如changeImage)中,更新@State变量。系统会自动检测状态变化并刷新UI。

    changeImage() {
      // 示例:在两张图片间轮换
      if (this.currentImage.id === $r('app.media.image1').id) {
        this.currentImage = $r('app.media.image2')
      } else {
        this.currentImage = $r('app.media.image1')
      }
    }
    

要点说明:

  • 状态驱动UI@State修饰的变量是响应式的。当其值改变时,所有依赖它的UI组件(本例中的Image)会自动更新。
  • 资源访问:使用$r('app.media.xxx')语法引用resources/base/media/目录下的图片资源。
  • 事件交互:通过组件的onClick事件绑定用户交互到状态变更函数。
  • 卡片开发规范:需在EntryFormAbility中创建FormBindingData来初始化卡片,并在form_config.json中配置卡片。上述UI与逻辑代码应编写在卡片的Page中。

此方法遵循HarmonyOS Next的ArkUI框架设计,通过状态管理实现了卡片内图片的动态切换。

回到顶部