HarmonyOS 离屏截图实战:createFromBuilder 动态生成图片的完整流程
文章目录
- 背景
- 方法总览
- 什么是离屏渲染?
- 第一步:定义顶层 @Builder 函数
- 第二步:调用 createFromBuilder
- checkImageStatus 参数的作用
- 截图结果显示
- delay 怎么设合适?
- 完整代码示例
- createFromBuilder vs get(),怎么选?
- 写在最后
背景
近期发现一款很有意思的HarmonyOS 三方库, 地址 @pura/harmony-utils(V1.4.0) , 作者是"桃花镇童长老", 我这里也是直接通过该作者公布的源码进行案例编写进行,写了到目前写了一部分demo ,感觉确实很有帮助,这里呢也是开始写一个系列的演示demo 供大家参考。如有帮助可以在OpenHarmony中进行下载安装进行使用哦
案例demo导航展示
↓↓↓↓↓↓接下来言归正传 ↓↓↓↓
上一篇讲了用get()截取已经显示在屏幕上的组件。但有时候你需要生成一张图片,这张图片对应的 UI 组件并不需要显示给用户看,只是用来生成图片内容——比如分享卡片、动态生成海报。
这种场景就需要用到createFromBuilder:离屏渲染。
方法总览
什么是离屏渲染?
正常的 UI 渲染是把组件画到屏幕上,用户能看到。离屏渲染是把组件"画"在一块内存缓冲区里,用户看不到,但我们可以把这块缓冲区的内容转成图片。
整个流程是:
- 定义一个
@Builder函数描述 UI 内容 - 调用
createFromBuilder,把这个 Builder 传进去 - 系统在内存里渲染这个 UI
- 截取内存里的内容,返回
PixelMap - 你拿到
PixelMap可以显示、保存、上传
第一步:定义顶层 @Builder 函数
关键点:createFromBuilder接受的是顶层@Builder函数(不是 struct 里的@Builder方法),必须定义在 struct 外面。
// 必须定义在组件 struct 外部@BuilderfunctionbuilderParam(){Column({space:6}){Text('SnapshotUtil 离屏渲染示例').fontSize(16).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)Text('createFromBuilder 截图内容').fontSize(12).fontColor('#CCFFCC')Row({space:6}){Circle({width:20,height:20}).fill('#FF6B6B')Circle({width:20,height:20}).fill('#4ECDC4')Circle({width:20,height:20}).fill('#FFE66D')}}.width(260).height(100).backgroundColor('#2C3E50').borderRadius(12).justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)}这个 Builder 定义了一张深色背景、有文字和彩色圆圈的小卡片。它不会显示在屏幕上,只用于离屏渲染。
第二步:调用 createFromBuilder
createFromBuilder接受三个参数:
- 第一个:
@Builder函数引用 - 第二个:delay(延迟毫秒数),让组件有时间完成渲染
- 第三个:checkImageStatus(是否检查图片加载状态)
this.Btn('createFromBuilder(builderParam, delay=300)','#9B59B6',()=>{SnapshotUtil.createFromBuilder(builderParam,300,false).then(pm=>{this.builderPixelMap=pm;this.addLog(`createFromBuilder() → PixelMap${pm.getPixelBytesNumber()}bytes`);}).catch((e:Error)=>{this.addLog(`createFromBuilder() Error:${e.message}`);});})delay=300的意思是等待 300 毫秒再截图,给系统时间完成布局和渲染。
checkImageStatus 参数的作用
如果 Builder 里包含Image组件(比如从网络加载的图片),图片加载是异步的,截图时图片可能还没加载完。这时候把checkImageStatus设为true,系统会等图片加载完毕再截图:
this.Btn('createFromBuilder(builderParam, delay=500, checkImageStatus=true)','#8E44AD',()=>{SnapshotUtil.createFromBuilder(builderParam,500,true).then(pm=>{this.builderPixelMap=pm;this.addLog(`createFromBuilder(delay=500) →${pm.getPixelBytesNumber()}bytes`);}).catch((e:Error)=>{this.addLog(`createFromBuilder Error:${e.message}`);});})delay=500, checkImageStatus=true:等 500ms 并检查图片状态,适合 Builder 里有网络图片的情况。
截图结果显示
拿到PixelMap后,用Image组件显示:
if(this.builderPixelMap!==undefined){this.SectionTitle('离屏截图预览')Image(this.builderPixelMap).width('100%').height(120).objectFit(ImageFit.Contain).backgroundColor('#F0F0F0').borderRadius(8)}delay 怎么设合适?
这个值没有固定标准,要根据 Builder 里的内容复杂程度来决定:
| Builder 内容 | 建议 delay |
|---|---|
| 只有文字和基本图形 | 200~300ms |
| 有本地图片资源 | 300~500ms |
| 有网络图片(配合 checkImageStatus=true) | 500~1000ms |
| 复杂布局+多张图片 | 800ms+ |
设太小可能截到空白内容,设太大用户等待时间长。建议先用 300ms 试,如果有空白内容就加长。
完整代码示例
import{SnapshotUtil}from'../Utils/SnapshotUtil';import{image}from'@kit.ImageKit';@Entry@Componentstruct CreateFromBuilderDemo{@StatebuilderPixelMap:image.PixelMap|undefined=undefined;build(){Column({space:12}){Button('生成离屏截图').onClick(()=>{SnapshotUtil.createFromBuilder(builderParam,300,false).then(pm=>{this.builderPixelMap=pm;}).catch((e:Error)=>{console.error('离屏截图失败:',e.message);});})if(this.builderPixelMap!==undefined){Text('离屏截图结果:').fontSize(13).fontColor('#888')Image(this.builderPixelMap).width('100%').height(120).objectFit(ImageFit.Contain).backgroundColor('#F0F0F0').borderRadius(8)}}.padding(16)}}// Builder 必须定义在 struct 外部@BuilderfunctionbuilderParam(){Column({space:6}){Text('SnapshotUtil 离屏渲染示例').fontSize(16).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)Text('createFromBuilder 截图内容').fontSize(12).fontColor('#CCFFCC')Row({space:6}){Circle({width:20,height:20}).fill('#FF6B6B')Circle({width:20,height:20}).fill('#4ECDC4')Circle({width:20,height:20}).fill('#FFE66D')}}.width(260).height(100).backgroundColor('#2C3E50').borderRadius(12).justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)}createFromBuilder vs get(),怎么选?
| 场景 | 推荐方法 |
|---|---|
| 截取屏幕上已有的组件 | get(id) |
| 动态生成不需要显示的图片 | createFromBuilder |
| 生成分享海报、卡片图 | createFromBuilder |
| 截取当前页面某个区域 | get(id) |
写在最后
createFromBuilder是生成动态图片的利器,特别适合"分享给朋友"这种功能——生成一张带用户数据的卡片图,直接分享,不需要专门做一个页面让用户看。
掌握这个方法,很多"截图生成"需求都能轻松搞定。
