从零开始开发纯血鸿蒙应用之无框截图
从零开始开发纯血鸿蒙应用
- 〇、前言
- 二、元素定位
- 1、理论依据
- 2、使用指导
- 三、认识 ComponentSnapshot
- 1、get 方法
- 2、获取 ComponentSnapshot 实例
- 四、实现组件截图
- 1、掌握图片编码能力
- 2、保存到图库
- 3、实现组件截图
〇、前言
截图,或者说截屏,已经是每部手机必备的功能了,通常使用关机键+音量减键触发,只是这种截屏会将整个屏幕、包括消息提示横幅弹窗、任务栏状态……总而言之,就是屏幕所显示的一切内容一并截进去,如果你只是像截取指定应用的指定页面的指定区域的内容,这种系统截屏功能就无法满足了。
在鸿蒙 NEXT 系统中,允许应用对自身进行组件截图,也就是局部区域截图,这就有效地避免截图时,将本不该截取的屏幕内容叶一并截取,不得不在截完图之后重新进行一番图片编辑、或者不得不重新截图。
下面就让我来说明一下,如何在纯血鸿蒙应用中集成内容截图功能。
二、元素定位
1、理论依据
想要对某一时刻屏幕上所显示的所有内容中,截取指定区域的内容,就需要一个充当内容定位的标识存在,比较繁琐的,可以利用屏幕坐标空间进行定位,但这种定位方式就跟冬天洗澡调水温一样,很不容易精确铆定到目标区域。
比较方便的就是,构建UI的结构体系支持赋予元素一个全局唯一的身份标识,也就是组件ID。实际上,在 ArkUI 中,内置组件是都有 ID 这个属性的:
2、使用指导
之前的篇章中,由于不涉及此方面的操作,所以一只没有使用鸿蒙 UI 组件的 ID 属性,现在,为了实现组件截图就必须使用上该属性了。
ArkUI 组件的 ID,使用起来非常简便,只需类似这样的代码即可:Text(this.fileContent).id(this.snapshotID)
。
注:基于我个人的开发经验,非常建议大家不要用字符串字面量,去给组件的ID属性赋值,从代码管理的角度,可以用一个专门的代码文件,集中存放各种ID值,如果只是在某个UI视图中使用,那么也应该以字段的形式进行存放。
由于组件截图的触发,不可能是页面视图一加载就触发,往往是用按钮或菜单项进行触发,而这些案触操作的处理函数,往往会抽取封装成私有方法或公共方法,因此,最好就是根据组件截图的生效方法,将对应组件的ID值用变量字段进行保存,代码示例中就是这样的。
三、认识 ComponentSnapshot
在鸿蒙 API 的诸多模块中,有一个@ohos.arkui.componentSnapshot
模块,该模块有如下官方描述:
本模块提供获取组件截图的能力,包括已加载的组件的截图和没有加载的组件的截图。组件截图只能够截取组件大小的区域,如果组件的绘制超出了它的区域,或子组件的绘制超出了父组件的区域,这些在组件区域外绘制的内容不会在截图中呈现。兄弟节点堆叠在组件区域内,截图不会显示兄弟组件。
简单说,就是为了方便大家进行组件截图而封装的API模块。
1、get 方法
在 ComponentSnapshot 模块中,有一个 get
方法,也就是实现组件截图所必须的方法,它的声明原型如下:
一如既往,是一个异步处理过程,回调函数返回的 image.PixelMap
,是一个图片解码对象
,可以用 Image 组件直接显示,但是如果想保存成普通的图片文件,就需要进行图片编码
;而 SnapshotOptions 主要包括如下配置项:
2、获取 ComponentSnapshot 实例
ComponentSnapshot 对象实例,并不是通过 new 操作获取的,而是利用上下文 UIContext 获取的:
而 UIContext 的实例,也不是 new 出来的,是方舟 UI 引擎会默认分配在组件的 this 信息中的:
所以,在进行内容截图之前,写一句 this.getUIContext().getComponentSnapshot()
是少不了的,不过,基于函数式编程和链式调用的编程风范,可以不用将 this.getUIContext().getComponentSnapshot() 的返回值用字段接受,而是继续调用 ComponentSnapshot 的 get 方法,除非不止一次要用到 ComponentSnapshot 对象。
四、实现组件截图
有了上面的理论基础,现在就可以着手实现组件截图功能了,在我的 TxtEdit 项目中,该功能具体实现为对展示出来的文本文件内容进行截图并显示截图结果与保存到图库中。
1、掌握图片编码能力
正如前面所说,组件截图产生的不是一个可以直接进行保存操作的普通图片,而是图片解码后的数据,在鸿蒙系统这边,就是一个 PixelMap 对象:
如果仅仅只想展示组件截图结果,可以不对所产生的 PixelMap 进行进一步处理,因为 Image 组件支持直接显示 PixelMap:
反之,如果需要支持保存到图库的操作,那么就需要进行编码,通俗点说,就是打包图片
,在 PixelMap 所在的 @ohos.multimedia.image
模块中,有一个 ImagePacker
对象,这种鸿蒙框架提供的专门进行图片打包处理用的API:
其下有一组针对不同打包操作而实现的 API:
如果想直接打包成文件,那么就可以使用 packToFile
方法,而我所采用的也正是这种方式:
如上所述,ImagePacker 实例是用专门的方法创建的,典型的工厂模式
。
2、保存到图库
目前,官方推荐图片保存到图库的功能,使用 SaveButton
进行实现:
从而不用在 AGC 也即鸿蒙应用市场上,专门为自己的应用申请 ACL 权限:ohos.permission.WRITE_IMAGEVIDEO
。
具体逻辑如下:
然而,不知道是系统升级引入新缺陷,还是其他我尚未获悉的缘故,目前,用上述代码实现的图片保存到图库的功能,并没如我预期般的表现,写入图库中的图片是空白无一物的,针对该异常,我已经在华为开发者官网上提了工单,但很可惜的是,平台支持力度并不是很理想,工单回复满是敷衍,既然贴了原来那种必须使用 ohos.permission.WRITE_IMAGEVIDEO 权限才能使用的代码:
真的是有点贻笑大方了,特别是结合如下的官方文档提示:
既然工单处理人员如此一反官方推荐,我也只会跟随着一反官方推荐,去AGC 上申请 ACL 权限:
只希望最后都有所进展吧,不然就只能砍掉组件截图并保存到图库中的功能了。
3、实现组件截图
上面的代码,都是组件截图完成后的进一步处理,而下面这段代码,才是实现组件截图的:
但组件截图顺利完成,就会打开一个预览弹窗,然后弹窗上的安全控件按钮,给用户一个保存到图库中的选择。
组件截图之后的图片内容,其实也可以直接回显在页面视图上,只要页面视图的空间允许,可以不用专门弹窗预览。
实际上,组件截图用在视频播放组件上,会有更好的表现,就是达到无边框截图的效果,只截取视频内容本身。