HarmonyOS Next 实战卡片开发 02
HarmonyOS Next 实战卡片开发 02
卡片开发中,还有一个难点是显示图片。其中分为显示本地图片和显示网络图片
显示本地图片
卡片可以显示本地图片,如存放在应用临时目录下的图片。路径比如
/data/app/el2/100/base/你的项目 boundleName/temp/123.png
以下操作是为了得到一张 该目录下的图片做的准备工作
- 截图,得到一张相册图片
- 使用PhotoViewPicker来选择要操作的图片
- 复制该图片到应用的临时目录下
- 传递给卡片组件
截图,得到一张相册图片
使用 PhotoViewPicker 来选择要操作的图片
在首页中,选择要操作的图片,获得该文件的 uri 地址
entry/src/main/ets/pages/Index.ets
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { fileIo } from '@kit.CoreFileKit';
import { promptAction } from '@kit.ArkUI';
@Entry
@Component
struct Index {
async aboutToAppear() {
// 创建一个新的 PhotoSelectOptions 实例来配置图片选择器的行为
let PhotoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
// 设置 MIME 类型为图像类型,这样用户只能选择图像文件
PhotoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
// 设置用户可以选择的最大图片数量为 1 张
PhotoSelectOptions.maxSelectNumber = 1;
// 创建一个新的 PhotoViewPicker 实例,用于打开图片选择器
let photoPicker = new photoAccessHelper.PhotoViewPicker();
// 使用前面配置好的选项打开图片选择器,并等待用户完成选择
// 注意这里的 select 方法是一个异步方法,所以需要使用 await 关键字等待其结果
const PhotoSelectResult = await photoPicker.select(PhotoSelectOptions);
// 获取用户选择的第一张图片的 URI(统一资源标识符)
// 假设这里只关心用户选择的第一张图片
// uri file://media/Photo/3/IMG_1729864738_002/screenshot_20241025_215718.jpg
const uri = PhotoSelectResult.photoUris[0];
promptAction.showToast({ message: `${uri}` })
}
build() {
RelativeContainer() {
}
.height('100%')
.width('100%')
}
}
复制该图片到应用的临时目录下
目标是将刚才的图片复制到应用的临时目录下,为最后的卡片显示本地图片做准备
在刚才的代码下,接着实现
// 获取应用的临时目录
let tempDir = getContext(this).getApplicationContext().tempDir;
// 生成一个新的文件名
const fileName = 123 + ".png";
// 通过缓存路径+文件名 拼接出完整的路径
const copyFilePath = tempDir + "/" + fileName;
// 将文件 拷贝到 临时目录
const file = fileIo.openSync(uri, fileIo.OpenMode.READ_ONLY);
fileIo.copyFileSync(file.fd, copyFilePath);
传递给卡片组件
在当前的环节中,有一个需要特别需要注意的地方,就是构造合适的数据。比如以下的数据
然后在创建卡片的时候,在 Ability 中读取图片地址,拼接参数,传递给卡片
entry/src/main/ets/entryformability/EntryFormAbility.ets
import { Want } from "@kit.AbilityKit";
import { fileIo } from "@kit.CoreFileKit";
import { formBindingData, FormExtensionAbility } from "@kit.FormKit";
export default class EntryFormAbility extends FormExtensionAbility {
// 在添加卡片时,打开一个本地图片并将图片内容传递给卡片页面显示
onAddForm(want: Want): formBindingData.FormBindingData {
// 假设在当前卡片应用的tmp目录下有一个本地图片 123.png
let tempDir = this.context.getApplicationContext().tempDir;
let imgMap: Record<string, number> = {};
// 打开本地图片并获取其打开后的fd
let file = fileIo.openSync(tempDir + "/" + "123.png");
imgMap["imgBear"] = file.fd;
class FormDataClass {
// 卡片需要显示图片场景, 必须和下列字段formImages 中的key 'imgBear' 相同。
imgName: string = "imgBear";
// 卡片需要显示图片场景, 必填字段(formImages 不可缺省或改名), 'imgBear' 对应 fd
formImages: Record<string, number> = imgMap;
}
let formData = new FormDataClass();
console.log("formDataformData", JSON.stringify(formData));
// 将fd封装在formData中并返回至卡片页面
return formBindingData.createFormBindingData(formData);
}
}
卡片需要 使用 'memory://+this.imgName'
来显示图片
let storageWidgetImageUpdate = new LocalStorage(); @Entry(storageWidgetImageUpdate) @Component struct WidgetImageUpdateCard { @LocalStorageProp('imgName') imgName: ResourceStr = ""; build() { Column() { } .width('100%').height('100%') .backgroundImage('memory://' + this.imgName) .backgroundImageSize(ImageSize.Cover) } }
完整效果
小结
截图,得到一张相册图片使用PhotoViewPicker来选择要操作的图片复制该图片到应用的临时目录下- 传递给卡片组件
以上的前三步骤都是为了得到临时图片,实际开发中根据情况来获取即可
还有
- Image 组件通过入参(memory://fileName)中的(memory://)标识来进行远端内存图片显示,其中 fileName 需要和 EntryFormAbility 传递对象('formImages': {key: fd})中的 key 相对应。
- Image 组件通过传入的参数是否有变化来决定是否刷新图片,因此 EntryFormAbility 每次传递过来的 imgName 都需要不同,连续传递两个相同的 imgName 时,图片不会刷新。
- 在卡片上展示的图片,大小需要控制在 2MB 以内。
显示网络图片
卡片中不支持直接显示网络图片如以下方式是不支持的
Image("https://cn-assets.gitee.com/assets/mini_app-e5eee5a21c552b69ae6bf2cf87406b59.jpg")
我们需要申请网络权限,然后将图片下载下来,最后再重复类似卡片显示本地图片的步骤即可
-
申请网络权限
entry/src/main/module.json5
-
设置网络图片地址
-
使用 http 开始下载
-
写入文件
-
返回给卡片组件
entry/src/main/ets/entryformability/EntryFormAbility.ets
import { Want } from "@kit.AbilityKit";
import { fileIo } from "@kit.CoreFileKit";
import {
formBindingData,
FormExtensionAbility,
formInfo,
formProvider,
} from "@kit.FormKit";
import { http } from "@kit.NetworkKit";
import { BusinessError } from "@kit.BasicServicesKit";
export default class EntryFormAbility extends FormExtensionAbility {
// 在添加卡片时,打开一个本地图片并将图片内容传递给卡片页面显示
onAddForm(want: Want) {
// 注意:FormExtensionAbility在触发生命周期回调时被拉起,仅能在后台存在5秒
// 建议下载能快速下载完成的小文件,如在5秒内未下载完成,则此次网络图片无法刷新至卡片页面上
const formId = want.parameters![formInfo.FormParam.IDENTITY_KEY] as string;
let netFile =
"https://env-00jxhf99mujs.normal.cloudstatic.cn/card/3.webp?expire_at=1729871552&er_sign=0eb3f6ac3730703039b1565b6d3e59ad"; // 需要在此处使用真实的网络图片下载链接
let httpRequest = http.createHttp();
// 下载图片
httpRequest
.request(netFile)
.then(async (data) => {
if (data?.responseCode == http.ResponseCode.OK) {
// 拼接图片地址
let tempDir = this.context.getApplicationContext().tempDir;
let fileName = "file" + Date.now();
let tmpFile = tempDir + "/" + fileName;
let imgMap: Record<string, number> = {};
class FormDataClass {
// 卡片需要显示图片场景, 必须和下列字段formImages 中的key fileName 相同。
imgName: string = fileName;
// 卡片需要显示图片场景, 必填字段(formImages 不可缺省或改名), fileName 对应 fd
formImages: Record<string, number> = imgMap;
}
// 打开文件
let imgFile = fileIo.openSync(
tmpFile,
fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE
);
imgMap[fileName] = imgFile.fd;
// 写入文件
await fileIo.write(imgFile.fd, data.result as ArrayBuffer);
let formData = new FormDataClass();
let formInfo = formBindingData.createFormBindingData(formData);
// 下载完网络图片后,再传递给卡片
formProvider.updateForm(formId, formInfo);
fileIo.closeSync(imgFile);
httpRequest.destroy();
console.log("============");
}
})
.catch((e: BusinessError) => {
console.log("eeee", e.message);
});
class FormData {
formId: string = "";
}
// 先返回基本数据
return formBindingData.createFormBindingData(new FormData());
}
onFormEvent(formId: string, message: string): void {}
}