HarmonyOS(50) 截图保存功能实现

news2024/9/22 3:43:31

componentSnapshot实现截图

  • 前言
  • 权限配置和申请
    • 权限配置
    • 权限申请
  • componentSnapshot截图实现
  • 将PixelMap转换成图片格式
  • 保存截图到系统相册
  • 保存截图到应用沙箱
  • 全部源码
  • 参考资料

前言

HarmonyOS提供了componentSnapshot实现组件截图功能,可以将UI截图成为image.PixelMap,然后可以将PixlMap保存到本地,运行效果如下图:
在这里插入图片描述
通过这篇博文你可以了解到:

  • HarmonyOS的权限申请方法
  • 截图功能的具体实现实现方法
  • 截图PixelMap转换成图片格式的方法
  • 异步和并发的简单使用
  • 将截图保存到系统相册的实现方法
  • 将截图保存到应用沙箱的实现方法

权限配置和申请

截图保存的功能,截图需要使用componentSnapshot,保存到系统相册需要在module.json5配置权限限“ohos.permission.WRITE_IMAGEVIDEO”

权限配置

在这里插入图片描述

    "requestPermissions": [
      {
        "name": "ohos.permission.WRITE_IMAGEVIDEO",
        "reason": "$string:reason",
        "usedScene": {
          "abilities": [
            "EntryAbility"
          ],
          "when": "inuse"
        }
      }
    ]

配置权限有个when属性,其有两个值:inuse(使用时)、always(始终),其他相关参数详见官方文档
在这里插入图片描述

权限申请

const permissions: Array<Permissions> = ['ohos.permission.WRITE_IMAGEVIDEO'];
function reqPermissionsFromUser(permissions: Array<Permissions>, context: common.UIAbilityContext): void {
  let atManager: abilityAccessCtrl.AtManager =
    abilityAccessCtrl.createAtManager();
  // requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗
  atManager.requestPermissionsFromUser(context, permissions).then((data) => {
    let grantStatus: Array<number> = data.authResults;
    let length: number = grantStatus.length;
    for (let i = 0; i < length; i++) {
      if (grantStatus[i] === 0) {
        // 用户授权,可以继续访问目标操作
      } else {
        // 用户拒绝授权,提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限
        return;
      }
    }
    // 授权成功
  }).catch((err: BusinessError) => {
    console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);
  })
}

上述方法可以在两处地方调用:在UIAbility和在UI中向用户申请授权。

  • 在UIAbility申请权限,在 windowStage.loadContent完成后申请
// 使用UIExtensionAbility:将 UIAbility 替换为UIExtensionAbility
export default class EntryAbility extends UIAbility {
  onWindowStageCreate(windowStage: window.WindowStage): void {
    // ...
    windowStage.loadContent('pages/Index', (err, data) => {
      reqPermissionsFromUser(permissions, this.context);
    // ...
    });
  }

  // ...
}
  • 在UI中申请权限,可以在aboutToApper方法中申请


struct Index {
  aboutToAppear() {
    // 使用UIExtensionAbility:将common.UIAbilityContext 替换为common.UIExtensionContext
    const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
    reqPermissionsFromUser(permissions, context);
  }

  build() {
    // ...
  }
}

向用户申请权限的更多细节,参考官方文档

componentSnapshot截图实现

该组件使用起来很简单,比如给组件设置一个id,将id传给componentSnapshot.get即可实现,代码如下,实现效果见文章开头gif图片。
在这里插入图片描述
详细的截图功能,参考官方文档。

将PixelMap转换成图片格式

将PixelMap转成图片格式,因为是耗时任务,所以使用异步操作,使用Promise保存返回值,Promise是一种用于处理异步操作的对象,可以将异步操作转换为类似于同步操作的风格,以方便代码编写和维护。Promise提供了一个状态机制来管理异步操作的不同阶段,并提供了一些方法来注册回调函数以处理异步操作的成功或失败的结果,详见异步并发概述 (Promise和async/await)。

  // 将pixelMap转成图片格式
  transferPixelMap2Buffer(pixelMap: image.PixelMap): Promise<ArrayBuffer> {
    return new Promise((resolve, reject) => {
      /**
       * 设置打包参数
       * format:图片打包格式,只支持 jpg 和 webp
       * quality:JPEG 编码输出图片质量 0-100
       * bufferSize:图片大小,默认 10M
       */
      let packOpts: image.PackingOption = { format: 'image/jpeg', quality: 98 };
      // 创建ImagePacker实例
      const imagePackerApi = image.createImagePacker();
      imagePackerApi.packing(pixelMap, packOpts).then((buffer: ArrayBuffer) => {
        resolve(buffer);
      }).catch((err: BusinessError) => {
        reject();
      })
    })
  }

保存截图到系统相册

注意使用async修饰,设置为异步执行。async/await是一种用于处理异步操作的Promise语法糖,通过使用async关键字声明一个函数为异步函数,并使用await关键字等待Promise的解析(完成或拒绝),以同步的方式编写异步操作的代码。async函数是一个返回Promise对象的函数,用于表示一个异步操作。在async函数内部,可以使用await关键字等待一个Promise对象的解析,并返回其解析值,注意下面代码最后一行 fileIo.close(file.fd)返回的是Promise<void>

  // 保存到系统相册
  async savePixmap2SysHelper() {
    if (!this.pixmap) {
      return;
    }
    const imgBuffer = await this.transferPixelMap2Buffer(this.pixmap);
    // 获取相册管理模块的实例,用于访问和修改相册中的媒体文件。
    let helper = photoAccessHelper.getPhotoAccessHelper(getContext(this));
    // 指定待创建的文件类型和后缀,创建图片或视频资源,使用callback方式返回结果。
    const uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'png');
    const file = await fileIo.open(uri, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
    await fileIo.write(file.fd, imgBuffer);
    //注意declare function close(file: number | File): Promise<void>
    await fileIo.close(file.fd);
  }

保存截图到应用沙箱

应用沙箱是一种以安全防护为目的的隔离机制,避免数据受到恶意路径穿越访问。在这种沙箱的保护机制下,应用可见的目录范围即为“应用沙箱目录”,详见官方文档

在这里插入图片描述

   context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
  //获取文件目录
   filesDir: string = this.context.filesDir;
  // 保存到应用沙箱
  async savePixmap2SystemFileManager() {

    if (!this.pixmap) {
      return;
    }
    const imgBuffer = await this.transferPixelMap2Buffer(this.pixmap);
    const file = fileIo.openSync(this.filesDir + `/${DateUtil.getTimeStamp()}.png`,
      fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
    await fileIo.write(file.fd, imgBuffer);
    await fileIo.close(file.fd);
  }

上述代码中其中filesDir获取的是应用通用文件路径,随应用卸载而清理。可以用于保存应用的任何私有数据,主要包括用户持久性文件、图片、媒体文件以及日志文件等。此路径下存储这些数据,使得数据保持私有、安全且持久有效

全部源码

import { componentSnapshot } from '@kit.ArkUI';
import { image } from '@kit.ImageKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { abilityAccessCtrl, common, Permissions } from '@kit.AbilityKit';
import { intl } from '@kit.LocalizationKit';
import { fileIo } from '@kit.CoreFileKit';
const permissions: Array<Permissions> = ['ohos.permission.WRITE_IMAGEVIDEO'];
function reqPermissionsFromUser(permissions: Array<Permissions>, context: common.UIAbilityContext): void {
  let atManager: abilityAccessCtrl.AtManager =
    abilityAccessCtrl.createAtManager();
  // requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗
  atManager.requestPermissionsFromUser(context, permissions).then((data) => {
    let grantStatus: Array<number> = data.authResults;
    let length: number = grantStatus.length;
    for (let i = 0; i < length; i++) {
      if (grantStatus[i] === 0) {
        // 用户授权,可以继续访问目标操作
      } else {
        // 用户拒绝授权,提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限
        return;
      }
    }
    // 授权成功
  }).catch((err: BusinessError) => {
    console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);
  })
}


export struct ComponentScreenshot {
   message: string = 'Hello World';
   timerSecond: number = 20;
   pixmap: image.PixelMap | null = null;
   isAutoPlay: boolean = true;
   showControls: boolean = false;
   curRate: number = 1;
   context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
   filesDir: string = this.context.filesDir;
  // controller: VideoController = new VideoController();
  aboutToAppear(): void {
    reqPermissionsFromUser(permissions, this.context);
  }
  // 组件截图
  clickToComponentSnapshot() {
    componentSnapshot.get('root', (error: Error, pixmap: image.PixelMap) => {
      if (error) {
        console.log('error: ' + JSON.stringify(error));
        return;
      }
      console.log('截图成功');
      this.pixmap = pixmap;
    })
  }
  // 保存到系统相册
  async savePixmap2SysHelper() {
    if (!this.pixmap) {
      return;
    }
    const imgBuffer = await this.transferPixelMap2Buffer(this.pixmap);
    // 获取相册管理模块的实例,用于访问和修改相册中的媒体文件。
    let helper = photoAccessHelper.getPhotoAccessHelper(getContext(this));
    // 指定待创建的文件类型和后缀,创建图片或视频资源,使用callback方式返回结果。
    const uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'png');
    const file = await fileIo.open(uri, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
    await fileIo.write(file.fd, imgBuffer);
    await fileIo.close(file.fd);
  }
  // 保存到应用沙箱
  async savePixmap2SystemFileManager() {

    if (!this.pixmap) {
      return;
    }
    const imgBuffer = await this.transferPixelMap2Buffer(this.pixmap);
    console.log("fileDir=="+this.filesDir);
    const file = fileIo.openSync(this.filesDir + `/${DateUtil.getTimeStamp()}.png`,
      fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
    await fileIo.write(file.fd, imgBuffer);
    await fileIo.close(file.fd);
  }

  // 将pixelMap转成图片格式
  transferPixelMap2Buffer(pixelMap: image.PixelMap): Promise<ArrayBuffer> {
    return new Promise((resolve, reject) => {
      /**
       * 设置打包参数
       * format:图片打包格式,只支持 jpg 和 webp
       * quality:JPEG 编码输出图片质量
       * bufferSize:图片大小,默认 10M
       */
      let packOpts: image.PackingOption = { format: 'image/jpeg', quality: 98 };
      // 创建ImagePacker实例
      const imagePackerApi = image.createImagePacker();
      imagePackerApi.packing(pixelMap, packOpts).then((buffer: ArrayBuffer) => {
        resolve(buffer);
      }).catch((err: BusinessError) => {
        reject();
      })
    })
  }
  build() {
    Column() {
      Row()
        .height(40)
        .padding(5)
        .position({ x: 0, y: 0 })
        .width('100%')
        .zIndex(1)
        .justifyContent(FlexAlign.End)
        .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
      Text(this.message)
        .fontColor(Color.White)
        .fontSize(20)
        .margin(50)
      Button('点击进行组件截图')
        .fontSize(20)
        .margin(10)
        .width('80%')
        .position({
          x: 20,
          y: 500
        })
        .onClick(() => {
          this.clickToComponentSnapshot();
        })
      if (this.pixmap) {
        Button('保存到系统相册')
          .fontSize(20)
          .margin(10)
          .width('80%')
          .position({
            x: 20,
            y: 550
          })
          .onClick(() => {
            this.savePixmap2SysHelper();
          })
        Button('保存到应用沙箱')
          .fontSize(20)
          .margin(10)
          .width('80%')
          .position({
            x: 20,
            y: 600
          })
          .onClick(() => {
            this.savePixmap2SystemFileManager();
          })
      }
    }
    .id('root')
    .height('100%')
    .width('100%')
    .backgroundColor('#b5b5b5')
    .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
  }
}
class DateUtil {
  static getFormatTimeStr(timestamp: number) {
    const date = new Date(timestamp);
    let dateFormat3 = new intl.DateTimeFormat('zh-CN', {
      year: 'numeric',
      month: '2-digit',
      day: '2-digit',
      hour: '2-digit',
      minute: '2-digit',
      second: '2-digit'
    });
    let formattedDate3 = dateFormat3.format(date);
    return formattedDate3;
  }
  /* 获取当前时间戳 */
  static getTimeStamp(): number {
    const timeStamp = (new Date()).getTime();
    return timeStamp;
  }
}

参考资料

@ohos.screenshot (屏幕截图)
组件截图怎么将pixelMap存储到系统相册或应用沙箱
@ohos.arkui.componentSnapshot (组件截图)
权限配置相关字段说明
向用户申请授权
异步并发概述 (Promise和async/await)。
应用沙箱

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2037237.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

WMS助力企业数字化转型(七)

WMS系统可以帮助企业实现更精确的库存控制&#xff0c;避免库存积压和缺货现象。通过对历史数据的分析&#xff0c;企业可以预测需求趋势&#xff0c;优化库存结构&#xff0c;从而减少资金占用和运营成本。同时&#xff0c;WMS还支持与其他系统的无缝对接&#xff0c;如企业资…

限时营销与开源AI智能名片O2O商城小程序的深度融合:重塑线上促销策略的新视角

摘要&#xff1a;在数字化营销日益激烈的今天&#xff0c;限时促销活动作为吸引用户注意力、激发购买欲望的传统手段&#xff0c;面临着前所未有的挑战。随着线上营销活动的泛滥&#xff0c;消费者对传统折扣策略已逐渐产生疲劳与免疫。因此&#xff0c;探索一种更加高效、精准…

开放式耳机最不伤耳吗?舒适度高的几款精选蓝牙耳机

开放式耳机对耳朵的伤害相对较小&#xff0c;但不能说它是最不伤耳的耳机。 与传统入耳式耳机相比&#xff0c;开放式耳机的优点在于不会深入耳道&#xff0c;减少了对耳朵的压迫感和耳道内的压力&#xff0c;佩戴起来更加舒适。同时&#xff0c;开放式设计允许空气流通&#…

免费简单的制作3D卡通建模——Fuse软件和Readyplayer的使用介绍

最终效果 文章目录 最终效果一、使用Fuse软件去Steam下载安装捏人选择身体部位自定义人物细节参数换装贴图修改导出OBJ文件即可 二、使用ReadyplayerReadyplayer官网地址选择从模板开始&#xff0c;或者拍照选择图片进行捏脸将模型导入Unity通过Readyplayer官方插件导入模型通过…

白盒测试-发送请求-引出MockMvc源码类

白盒测试是什么&#xff1f; 一般是测开做白盒测试&#xff0c;研发做白盒 spring boot是和junit结合 原本是jmeter发请求&#xff0c;是看不到代码逻辑&#xff0c;有接口信息就可以实现 用测试代码发请求&#xff0c;能看到代码逻辑&#xff0c;比接口测试更全面&#xf…

大恒相机通过Line2或Line3直接给出3.3V触发,形成分时曝光

大恒相机通过Line2或Line3直接给出3.3V触发&#xff0c;形成分时曝光 一、分时曝光需求二、3.3V信号分时曝光设计 写在前面 上班了&#xff0c;没多少时间再去精度论文了&#xff0c;大多是项目上的事情。 一、分时曝光需求 一般的12V光源通过光源控制器与大恒相机Line1线连接…

PDF转换器推荐:轻松将图片批量转为PDF

高质量的图片与文档管理已经逐渐成为了我们日常工作中不可或缺的一部分。为了防止图片在传输的过程中被压缩&#xff0c;我经常将他们转换为PDF格式。这次我给你推荐几个我常用的图片转PDF的小工具吧。 1.福昕PDF转换大师 链接一下>>https://www.pdf365.cn/pdf2word/ …

关于自己部署AI大模型踩的坑(一)——硬件篇

最近一直在研究如何打算属于我自己的J.A.R.V.I.S.&#xff08;钢铁侠中的机器人管家&#xff09;。 上一篇写了我最近在部署自己的大模型&#xff0c;使用llama3.1&#xff0c; 和通义千问2。虽然最终结果也是成功了&#xff0c;过程却十分地坎坷。 所以这一篇文章一是总结其中…

keepalived基础

目录 1 高可用集群简介 1.1 高可用的概念 1.2 常见的 HA 集群 1.3 高可用集群软件 2 keepalived的功能与用途 2.1 LVS directors failover功能 2.2 LVS cluster nodes healthchecks功能 3 VRRP协议介绍 4 Keepalived 架构 5 原理总结 1 高可用集群简介 1.1 高可用的概念 高可…

数据埋点系列 5|数据驱动决策:形成数据驱动文化

在过去的几篇文章中&#xff0c;我们深入探讨了数据埋点、数据质量保证、数据分析和可视化等主题。现在&#xff0c;让我们站在更高的视角&#xff0c;讨论如何将这些技术和方法整合到实际的业务决策中&#xff0c;以及如何在组织中建立真正的数据驱动文化。 目录 1. 回顾&am…

Github 2024-08-14 C开源项目日报Top10

根据Github Trendings的统计,今日(2024-08-14统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量C项目10Objective-C项目1PHP项目1Python项目1PHP:流行的Web开发脚本语言 创建周期:4710 天开发语言:C, PHP协议类型:OtherStar数量:37340 …

【Pyspark-驯化】一文搞懂Pyspark修改hive表描述以及增加列使用技巧

【Pyspark-驯化】一文搞懂Pyspark修改hive表描述以及增加列使用技巧 本次修炼方法请往下查看 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我工作、学习、实践 IT领域、真诚分享 踩坑集合&#xff0c;智慧小天地&#xff01; &#x1f387; 相关内容文档获取 微…

C++11 STL中的Vector使用细节

容器 支持泛型 Vector常用成员函数示例迭代器操作插入和删除操作 与find 配合:vector 一些复杂操作 Vector 的内存管理策略压入对象 需要无参构造.压入对象指针 高效插入和删除 迭代器失效 代码优化: 二维及多维空间生成 容器 支持泛型 vector<int> vi;vector<double…

中国数据库的前世今生:披荆斩棘,乘风破浪

文章目录 前言国外数据库技术蓬勃发展中国信息化起步与发展&#xff08;数据库技术探索&#xff09;国外数据库商战策略解决燃眉之急学习先进技术 数据库技术的新格局雷声大雨点小的千年虫新型数据库的诞生国产数据库展露头脚 开源助力国产数据库弯道超车去“IOE”化大数据席卷…

使用OIDC登录kubesphere遇到的坑细节

1.通过代理telepresence到本地调试&#xff0c;使用默认账号密码&#xff0c;提示账号密码错误。 2.kubesphere在sso登录的时候&#xff0c;提示签名错误&#xff0c;其实这个错误不是很明确&#xff0c;所以要到本地调试&#xff0c;找到根本原因。 # 错误1 Tnauthorized: fa…

《Linux运维总结:基于ARM64架构CPU使用docker-compose一键离线部署etcd 3.5.15容器版分布式集群》

总结&#xff1a;整理不易&#xff0c;如果对你有帮助&#xff0c;可否点赞关注一下&#xff1f; 更多详细内容请参考&#xff1a;《Linux运维篇&#xff1a;Linux系统运维指南》 一、部署背景 由于业务系统的特殊性&#xff0c;我们需要面对不同的客户部署业务系统&#xff0…

skywalking架构

1.整体架构 整个架构&#xff0c;分成上、下、左、右四部分&#xff1a; 在SkyWalking中&#xff0c;trace 数据和 metrics 数据是两种关键的监控数据类型&#xff0c;它们帮助你理解应用的运行状态、性能瓶颈和故障点 1.1Trace 数据 Trace 数据是分布式追踪数据&#xff0c;它…

【原创】java+springboot+mysql学业跟踪指导管理系统设计与实现

个人主页&#xff1a;程序猿小小杨 个人简介&#xff1a;从事开发多年&#xff0c;Java、Php、Python、前端开发均有涉猎 博客内容&#xff1a;Java项目实战、项目演示、技术分享 文末有作者名片&#xff0c;希望和大家一起共同进步&#xff0c;你只管努力&#xff0c;剩下的交…

Centos7安装jdk1.6(jdk-6u45-linux-x64.bin)

目录 一&#xff0c;查看是否安装jdk&#xff1a;二&#xff0c;下载安装文件&#xff1a;三&#xff0c;开始安装a. jdk-6u45-linux-x64-rpm.bin文件安装&#xff1a;b. jdk-6u45-linux-x64.bin文件安装【建议安装此文件】&#xff1a; 四&#xff0c;配置环境变量&#xff1a…

37.docker拉取镜像失败和创建mysql5.6容器

问题 docker pull 拉取镜像文件超时&#xff0c;网络连接不上。 解决方案 在/etc/docker目录下创建daemon.json文件。 文件内容为&#xff1a; { "builder": { "gc": { "defaultKeepStorage": "20GB", "en…