HarmonyOS Next 实战卡片开发 02

简介: 本文介绍了 HarmonyOS Next 实战中卡片开发的图片显示技术,包括本地图片和网络图片的处理方法。对于本地图片,通过截图、选择图片、复制到临时目录并传递给卡片组件完成显示;而对于网络图片,则需申请网络权限,下载图片至本地后再按本地图片流程处理。文中详细展示了代码实现步骤与关键点,如使用 `PhotoViewPicker` 选择图片、`http` 下载网络资源以及通过 `formImages` 传递图片数据,确保图片在卡片中正确显示。

HarmonyOS Next 实战卡片开发 02

卡片开发中,还有一个难点是显示图片。其中分为显示本地图片和显示网络图片

显示本地图片

卡片可以显示本地图片,如存放在应用临时目录下的图片。路径比如

/data/app/el2/100/base/你的项目 boundleName/temp/123.png

以下操作是为了得到一张 该目录下的图片做的准备工作

  1. 截图,得到一张相册图片
  2. 使用PhotoViewPicker来选择要操作的图片
  3. 复制该图片到应用的临时目录下
  4. 传递给卡片组件

image-20241025215452668

截图,得到一张相册图片

使用 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%')
  }
}

复制该图片到应用的临时目录下

目标是将刚才的图片复制到应用的临时目录下,为最后的卡片显示本地图片做准备

image-20241025220456712

在刚才的代码下,接着实现

// 获取应用的临时目录
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);

传递给卡片组件

在当前的环节中,有一个需要特别需要注意的地方,就是构造合适的数据。比如以下的数据

image-20241025221558917

然后在创建卡片的时候,在 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)
  }
}

完整效果

小结

  1. 截图,得到一张相册图片
  2. 使用PhotoViewPicker来选择要操作的图片
  3. 复制该图片到应用的临时目录下
  4. 传递给卡片组件

以上的前三步骤都是为了得到临时图片,实际开发中根据情况来获取即可

还有

  • 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")

我们需要申请网络权限,然后将图片下载下来,最后再重复类似卡片显示本地图片的步骤即可

  1. 申请网络权限

    entry/src/main/module.json5

    image-20241025225743062

  2. 设置网络图片地址

  3. 使用 http 开始下载

  4. 写入文件

  5. 返回给卡片组件

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 {
   }
}

目录
相关文章
|
9天前
|
定位技术 UED
70. [HarmonyOS NEXT 实战案例九] 旅游景点网格布局(下)
在上一篇教程中,我们学习了如何使用GridRow和GridCol组件实现基本的旅游景点网格布局。本篇教程将在此基础上,深入探讨如何优化布局、添加交互功能,以及实现更多高级特性,打造一个功能完善的旅游景点应用。
24 1
|
9天前
|
容器
69.[HarmonyOS NEXT 实战案例九] 旅游景点网格布局(上)
本教程将详细讲解如何使用HarmonyOS NEXT中的GridRow和GridCol组件实现旅游景点网格布局。通过网格布局,我们可以以美观、规整的方式展示各种旅游景点信息,为用户提供良好的浏览体验。
22 1
|
9天前
|
UED
68.[HarmonyOS NEXT 实战案例八] 电影票务网格布局(下)
在上一篇教程中,我们学习了如何使用GridRow和GridCol组件实现基本的电影票务网格布局。本篇教程将在此基础上,深入探讨如何优化布局、添加交互功能,以及实现更多高级特性,打造一个功能完善的电影票务应用。
25 1
|
9天前
|
开发者 容器
67.[HarmonyOS NEXT 实战案例八] 电影票务网格布局(上)
在移动应用开发中,电影票务应用是一个常见的场景,用户可以通过应用查看正在热映的电影信息,并进行选座购票等操作。本教程将详细讲解如何使用HarmonyOS NEXT的GridRow和GridCol组件实现电影票务应用中的电影列表网格布局,帮助开发者掌握网格布局的基本用法和实现技巧。
23 1
|
17天前
|
开发框架 前端开发 JavaScript
【HarmonyOS Next之旅】基于ArkTS开发(二) -> UI开发一
本文介绍了方舟开发框架(ArkUI)及其两种开发范式:基于ArkTS的声明式开发范式和类Web开发范式。ArkUI是用于构建HarmonyOS应用界面的UI框架,提供极简UI语法和基础设施。声明式开发范式使用ArkTS语言,以组件、动画和状态管理为核心,适合复杂团队协作;类Web开发范式采用HML、CSS、JavaScript三段式开发,适用于简单界面应用,贴近Web开发者习惯。文中还概述了两者的架构和基础能力,帮助开发者选择合适的范式进行高效开发。
67 15
|
17天前
|
编解码 前端开发 Java
【HarmonyOS Next之旅】基于ArkTS开发(二) -> UI开发三
本文介绍了基于声明式UI范式的图形绘制与动画效果实现方法,涵盖绘制图形、添加动画效果及常见组件说明三部分内容。在绘制图形部分,详细讲解了如何通过Circle组件为食物成分表添加圆形标签,以及使用Path组件结合SVG命令绘制自定义图形(如应用Logo)。动画效果部分则展示了如何利用animateTo实现闪屏动画,包括渐出、放大效果,并设置页面跳转;同时介绍了页面间共享元素转场动画的实现方式。最后,文章列举了声明式开发范式中的各类组件及其功能,帮助开发者快速上手构建复杂交互页面。
55 11
|
17天前
|
开发工具
鸿蒙开发:DevEcoStudio中的代码生成
其实大家可以发现,一篇文章下来,都是基于右键后的Generate选项,所以,还是非常的简单的,当然了,还是希望大家,以上的功能,能够应用在实际的开发中,而不是停留在纸面上。
鸿蒙开发:DevEcoStudio中的代码生成
|
13天前
|
UED 容器
5.HarmonyOS Next开发宝典:掌握Flex布局的艺术
Flex布局(弹性布局)是HarmonyOS Next中最强大的布局方式之一,它提供了一种更加高效、灵活的方式来对容器中的子元素进行排列、对齐和分配空间。无论是简单的居中显示,还是复杂的自适应界面,Flex布局都能轻松应对。
36 0
|
17天前
|
JavaScript 小程序 API
UniApp X:鸿蒙原生开发的机会与DCloud的崛起之路·优雅草卓伊凡
UniApp X:鸿蒙原生开发的机会与DCloud的崛起之路·优雅草卓伊凡
77 12
UniApp X:鸿蒙原生开发的机会与DCloud的崛起之路·优雅草卓伊凡
|
13天前
|
JSON IDE Java
鸿蒙开发:json转对象插件回来了
首先,我重新编译了插件,进行了上传,大家可以下载最新的安装包进行体验了,还是和以前一样,提供了在线版和IDE插件版,两个选择,最新的版本,除了升级了版本,兼容了最新的DevEco Studio ,还做了一层优化,就是针对嵌套对象和属性的生成,使用方式呢,一年前的文章中有过详细的概述,这里呢也简单介绍一下。
鸿蒙开发:json转对象插件回来了