Skip to content

组件截图并保存到相册

想做一个功能把一个组件保存到相册,当然组件不一定是整个屏幕的,而是需要保存下载的那一个部分。组件可能是多个组件的堆叠的结果。

查看了很多文档之后找到一个可行的方案,大体步骤就是先截图到一个 image的 pixelmap 里边,然后把这个 pixelmap 转换成需要保存的格式,譬如过jpg格式,然后保存到相册。

截图到 pixelMap

使用API componentSnapshot 可以根据组件ID把图片保存到一个pixelmap里边,所以说首先要给一个组件加上id属性。

javascript
componentSnapshot.get(id, async (error: Error, pixmap: image.PixelMap) => {
  if (error) {
    console.log("error: " + JSON.stringify(error))
    return;
  }

  // pixmap 准备后续的处理
})

图片编码成jpg

本例子以保存成jpg为例,设置一下图片质量,如果说保存的图片大小还是太大的话,可以先把图片的分辨率稍微降低一些,然后再次编码并设置更低的图片质量。

javascript
let imagePackerApi = image.createImagePacker();
let packOpts: image.PackingOption = { format: 'image/jpeg', quality: 98 };
imagePackerApi.packing(pixmap, packOpts).then(async (buffer: ArrayBuffer) => {
  // buffer 就是图片数据,可以直接保存
}).catch((error: BusinessError) => {
  console.error('Failed to pack the image. And the error is: ' + error);
})

保存图片到相册

保存图片到相册。先获取一个相册的文件地址,打开文件并且将图片数据写入文件。

javascript
try {
  const context = getContext(this);
  let helper = photoAccessHelper.getPhotoAccessHelper(context);
  let uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg');
  let file = await fs.open(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
  // 写入文件
  await fs.write(file.fd, buffer);
  promptAction.showToast({ message: '已保存至相册!' });
  // 关闭文件
  await fs.close(file.fd);
} catch (error) {
  console.error('error is ' + JSON.stringify(error));
}

不过由于现在鸿蒙系统对权限申请比较严格,如果直接调用这个函数的话,会因为ACL权限而失败,但是这个ACL权限需要发邮件申请白名单,不过理由不正当可能会审核不通过吧。不过研究了半天还有一个比较人性的方式,不过这样就是不太方便定制现在按钮样式。本例就是使用的这种方式。即安全组件方式。

安全组件

能够设置的东西比较少,大体上就是按钮的icon大小及颜色一些的。不过也还能用吧,毕竟这样就不需要发邮件申请ACL权限了。

javascript
SaveButton()
  .padding({left: 40, right: 40})
  .fontColor(Color.White)
  .fontWeight(FontWeight.Bold)
  .height(48)
  .borderRadius(24)
  .backgroundColor(Color.Blue)
  .onClick(async () => {
    this.saveUI('calendar')
  })

完整代码:

javascript
saveUI(id: string) {
  componentSnapshot.get(id, async (error: Error, pixmap: image.PixelMap) => {
    if (error) {
      console.log("error: " + JSON.stringify(error))
      return;
    }

    let imagePackerApi = image.createImagePacker();
    let packOpts: image.PackingOption = { format: 'image/jpeg', quality: 98 };
    imagePackerApi.packing(pixmap, packOpts).then(async (buffer: ArrayBuffer) => {
      try {
        const context = getContext(this);
        let helper = photoAccessHelper.getPhotoAccessHelper(context);
        let uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'png');
        let file = await fs.open(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
        // 写入文件
        await fs.write(file.fd, buffer);
        promptAction.showToast({ message: '已保存至相册!' });
        // 关闭文件
        await fs.close(file.fd);
      } catch (error) {
        console.error('error is ' + JSON.stringify(error));
      }
    }).catch((error: BusinessError) => {
      console.error('Failed to pack the image. And the error is: ' + error);
    })
    pixmap.release();
  })
}

build() {
  Column() {
    SaveButton()
        .padding({left: 40, right: 40})
        .fontColor(Color.White)
        .fontWeight(FontWeight.Bold)
        .height(48)
        .borderRadius(24)
        .backgroundColor(Color.Blue)
        .onClick(async () => {
          this.saveUI('calendar')
        })
  }
  .id('calendar')
}

结果如下图: