鸿蒙开发:分享弹窗

注:适用版本(HarmonyOS NEXT/5.0/API12+)

在鸿蒙应用开发里,实现图片分享功能,对提升用户体验有着极大的帮助。它能让用户更自由地处理图片,满足大家在不同场景下的需求。从应用自身价值来看,这个功能就像是给应用添了一对翅膀。而且,它还能增强用户对应用的依赖度,让用户更愿意留在这个应用里,从而提高应用的竞争力,在众多应用中脱颖而出。
这次分享一个只需要临时请求的保存权限(代码中颜色请自己替换谢谢)

分享弹窗组件

目的: 定义一个分享弹窗组件,点击分享打开
会使用到一个二维码组件 QRCode
实现步骤:

  1. 自定义Dialog
    a. 名称: QuestionShareDialog, 接收试题对象 item
    b. 获取用户信息
  2. 布局组件
    a. 堆叠容器 (底部对齐, 裁剪, 圆角)
    b. Column组件, Row组件

QuestionShareDialog.ets

import { UserStoreKey } from '../../commons/utils/Auth'
import { QuestionDetail, User } from '../../models'

@CustomDialog
export struct QuestionShareDialog {
  @Prop item: QuestionDetail
  @StorageProp(UserStoreKey) user: User = {} as User

  controller: CustomDialogController  //必须要有
  build() {
    Stack({ alignContent: Alignment.BottomEnd }) {
      Column({ space: 20 }) {
        Image($r('代码中图片请自己替换谢谢'))
          .width(40)
          .height(40)
        Text('搞定企业面试题')
        Divider()
          .strokeWidth(0.5)
          .color($r('代码中颜色请自己替换谢谢'))
        Text('大厂面试题:' + this.item.stem)
          .fontSize(12)
          .maxLines(2)
          .fontWeight(600)
          .width('100%')
          .lineHeight(24)
          .textOverflow({ overflow: TextOverflow.Ellipsis })
        QRCode(this.item.id)
          .width(160)
          .height(160)
          .alignSelf(ItemAlign.Center)
        Text('扫码查看答案')
          .fontSize(12)
          .alignSelf(ItemAlign.Center)
        Blank()
        Text('分享来自:' + this.user.nickName || this.user.username)
          .fontSize(12)
      }
      .id('share')  // 用于后续截图使用
      .padding(20)
      .alignItems(HorizontalAlign.Start)
      .width(300)
      .height(500)
      .backgroundColor($r('app.color.white'))


      Row() {
        Text('保存到本地')
          .fontColor($r('app.color.white'))
          .fontSize(14)
          .padding(12)
          .backgroundColor($r('代码中颜色请自己替换谢谢'))
      }
      .borderRadius({ topLeft: 8 })
      .clip(true)
    }
    .borderRadius(8)
    .clip(true)
  }
}

注:这里是自定义弹窗
QuestionPage.ets

  shareDialog = new CustomDialogController({
    builder: QuestionShareDialog({ item: this.item }),//内容
    customStyle: true,//开启自定义弹窗
    alignment: DialogAlignment.Center //位置
  })

在哪里使用点击他

      MenuItem({ content: '分享' })
        .onClick(() => this.shareDialog.open())

组件截屏

目标:分享弹窗截图并缓存到缓存目录
前置知识:
componentSnapShot

get(id: string, options?: SnapshotOptions): Promise<image.PixelMap>

获取已加载的组件的截图,传入组件的组件标识,找到对应组件进行截图。通过Promise返回结果。
ImagePacker

packing(source: PixelMap, option: PackingOption): Promise<ArrayBuffer>

图片压缩或重新打包,使用Promise形式返回结果。
实现步骤:

  • 方法名称: saveImage
  • 使用 componentSnapShot 组件截图的get方法,得到 PixelMap 像素图像数据
  • 使用 ImagePacker 打包 PixelMap 数据,转成二进制 ArrayBuffer 图片数据
  • 将 ArrayBuffer 图片数据写入缓存目录,生成图片
    • 获取上下文
    • 定义图片的存储路径 ctx.cacheDir + '/' + Date.now() + '.jpeg'
    • 通过fileIo以 创建读写 的方式打开文件
    • 通过fileIo同步把二进制数据写入文件
    • 同步关闭文件
    • 提示用户
  /**
 1. 保存图片到沙箱, 再从沙箱中读取写入相册
   */
  async saveImage() {
    // 1. 根据组件的id生成截图对象
    const pixelMap = await componentSnapshot.get('share')
    // 2. 借助ImagePacker去把图片对象生成二级制数据流
    const imagePacker = image.createImagePacker()
    const arrayBuffer = await imagePacker.packToData(pixelMap, { format: "image/jpeg", quality: 98 })
    // 3. 借助fileIo读写文件
    // 3.1 获取上下文
    const ctx = getContext(this)
    // 3.2 获取沙箱中存图的路径
    const imagePath = ctx.cacheDir + '/' + Date.now() + '.jpeg'
    // 3.3 以 创建 或 读写 的模式打开文件(没有则创建并打开, 有则打开)
    const file = fileIo.openSync(imagePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE)
    // 3.4 同步写入二级制数据流到文件中
    fileIo.writeSync(file.fd, arrayBuffer)
    // 3.5 同步去关闭文件
    fileIo.closeSync(file.fd)
    promptAction.showToast({ message: '保存成功' })
  }
        Text('保存到本地')
          .fontColor($r('app.color.white'))
          .fontSize(14)
          .padding(12)
          .backgroundColor($r('代码中颜色请自己替换谢谢'))
          .onClick(() => {
            this.saveImage()
          })

现在就保存到了沙箱中
沙箱在Device File Browser > data > app > el2 > 100 > base > 包名
如何寻找沙箱
如何寻找沙箱

保存到相册

目标:将沙箱缓存目录中的文件保存到相册
前置知识点:

  1. photoAccessHelper 该模块提供相册管理模块能力,包括创建相册以及访问、修改相册中的媒体数据信息等。
  2. SaveButton 安全控件的保存控件,用户通过点击该保存按钮,可以临时获取存储权限,而不需要权限弹框授权确认。

实现步骤:

  • 使用fileUri.getUriFromPath()获取uri地址
  • 使用 photoAccessHelper 模块发起资源变更请求,存储图片到相册
    • 创建实例对象 photoAccessHelper.getPhotoAccessHelper(ctx)
    • 调用方法 applyChanges
  • 使用 SaveButton 安全组件获取短时权限,进行相册操作
    • 判断用户成功授权
    • 处理按钮图标/文字/样式
...
// 3.5 同步去关闭文件
    fileIo.closeSync(file.fd)

    // 4. 把沙箱中的文件写入相册
    // 4.1 获取资源文件的uri地址
    const imgUrl = fileUri.getUriFromPath(imagePath)
    // 4.2 进行图片资产变更(私有->公有)
    const assetChangeRequest = photoAccessHelper.MediaAssetChangeRequest.createImageAssetRequest(ctx, imgUrl);
    // 4.3 提交媒体变更请求
    // 4.3.1 获取相册管理模块的实例
    const phAccessHelper = photoAccessHelper.getPhotoAccessHelper(ctx)
    // 4.3.2 调用变更方法
    // 需要配置权限: permission: ohos.permission.WRITE_IMAGEVIDEO
    await phAccessHelper.applyChanges(assetChangeRequest);

    // 5. 关闭弹窗
    this.controller.close()

    promptAction.showToast({ message: '图片写入相册成功' })

注:之前的Text换成SaveButton

  SaveButton({
    icon: SaveIconStyle.FULL_FILLED,//设置保存按钮的图标风格
    text: SaveDescription.SAVE_IMAGE,//设置保存按钮的文本描述
    buttonType: ButtonType.Normal//设置保存按钮的背景样式
  })
    .fontColor($r('app.color.white'))
    .fontSize(14)
    .padding(12)
    .backgroundColor($r('代码中颜色请自己替换谢谢'))
    .onClick((event: ClickEvent, result: SaveButtonOnClickResult) => {
    //如果用户点击授权
      if (result === SaveButtonOnClickResult.SUCCESS) {
        this.saveImage()
      }
    })

展望

展望未来,随着鸿蒙系统的不断发展和普及,图片保存和分享功能肯定会变得更强大、更便捷。从系统层面来看,可能会提供更简洁、更强大的 API 。比如说,在权限管理方面,以后说不定系统能自动帮我们处理权限申请的复杂逻辑,开发者不用再为权限申请的细节操心,只要专注于功能实现就好,这能大大提高开发效率,让开发过程变得更轻松。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐