三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

HarmonyOS 离屏截图实战:createFromBuilder 动态生成图片的完整流程

HarmonyOS 离屏截图实战:createFromBuilder 动态生成图片的完整流程

文章目录

    • 背景
      • 方法总览
      • 什么是离屏渲染?
      • 第一步:定义顶层 @Builder 函数
      • 第二步:调用 createFromBuilder
      • checkImageStatus 参数的作用
      • 截图结果显示
      • delay 怎么设合适?
      • 完整代码示例
      • createFromBuilder vs get(),怎么选?
      • 写在最后

背景

近期发现一款很有意思的HarmonyOS 三方库, 地址 @pura/harmony-utils(V1.4.0) , 作者是"桃花镇童长老", 我这里也是直接通过该作者公布的源码进行案例编写进行,写了到目前写了一部分demo ,感觉确实很有帮助,这里呢也是开始写一个系列的演示demo 供大家参考。如有帮助可以在OpenHarmony中进行下载安装进行使用哦

案例demo导航展示

↓↓↓↓↓↓接下来言归正传 ↓↓↓↓
上一篇讲了用get()截取已经显示在屏幕上的组件。但有时候你需要生成一张图片,这张图片对应的 UI 组件并不需要显示给用户看,只是用来生成图片内容——比如分享卡片、动态生成海报。

这种场景就需要用到createFromBuilder:离屏渲染。

方法总览

什么是离屏渲染?

正常的 UI 渲染是把组件画到屏幕上,用户能看到。离屏渲染是把组件"画"在一块内存缓冲区里,用户看不到,但我们可以把这块缓冲区的内容转成图片。

整个流程是:

  1. 定义一个@Builder函数描述 UI 内容
  2. 调用createFromBuilder,把这个 Builder 传进去
  3. 系统在内存里渲染这个 UI
  4. 截取内存里的内容,返回PixelMap
  5. 你拿到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是生成动态图片的利器,特别适合"分享给朋友"这种功能——生成一张带用户数据的卡片图,直接分享,不需要专门做一个页面让用户看。

掌握这个方法,很多"截图生成"需求都能轻松搞定。

← 返回列表