HarmonyOS鸿蒙Next中image.PixelMap通过wrapBuilder给到Image不展示问题

HarmonyOS鸿蒙Next中image.PixelMap通过wrapBuilder给到Image不展示问题 有人遇到过这问题吗,什么原因

11 回复

我测试了一下,直接传参的方式传递pixelMap确实不行,pixelMap是程序中真实的是内存地址,跨模块可能会有问题,可以采用共享内存或者文件路径的方式实现,我写了一个示例代码,可以参考一下:

import { image } from '@kit.ImageKit';

@Entry
@Component
struct TestHsp {
  @State message: string = 'Hello World';

  build() {
    Column() {
      Row() {
        SymbolGlyph($r("sys.symbol.arrow_left")).onClick(() => {
          this.getUIContext().getRouter().back()
        })
      }.width('100%').justifyContent(FlexAlign.Start)
      Row(){
        Button("打开HSP").onClick(()=>{
          let file = this.getUIContext().getHostContext()?.filesDir+"/"+"test.jpg"
          let imageSource = image.createImageSource(file)
          let pixelMap = imageSource.createPixelMapSync()
          AppStorage.setOrCreate('HspSharedPixelMap', pixelMap);
          this.getUIContext().getRouter().pushUrl({
            url: '@bundle:cn.com.demos/myHsp/ets/pages/Index',
            params: {
              filePath:file,
            }
          }).catch((err: BusinessError) => {
            console.error(`跳转失败, code: ${err.code}, message: ${err.message}`);
          });

        })
      }
    }
  }
}

HSP代码:

@Entry
@Component
struct Index {

  @State fileName:string=""

  @StorageLink("HspSharedPixelMap") image:PixelMap|undefined=undefined


  aboutToAppear(): void {
    const params = this.getUIContext().getRouter().getParams() as Record<string, Object>;

    if(params && params['filePath']){
      this.fileName = params['filePath'] as string
      console.log(`${this.fileName}`)
    }
  }

  build() {
    Column() {
      Image(this.image).width(200).height(200).objectFit(ImageFit.Contain)
      Image("file://"+this.fileName).width(200).height(200).objectFit(ImageFit.Contain)
      Button("返回").onClick(()=>{
        this.getUIContext().getRouter().back()
      })
    }
    .width('100%').height('100%')
  }
}

更多关于HarmonyOS鸿蒙Next中image.PixelMap通过wrapBuilder给到Image不展示问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


PixelMap 给到 Image 不展示,通常要同时看“数据是否有效”和“Builder 是否触发重建”。Image 本身支持 PixelMap,但 PixelMap 背后有 native 图像内存和生命周期,不像字符串那样随便跨层传都稳定。

建议按这个顺序查:

  1. 先在同一个组件里直接 Image(pixelMap) 验证 PixelMap 本体可显示;
  2. 异步拿到 PixelMap 后要更新 @State/@Trace 等能触发 UI 刷新的状态,不要只改普通成员变量;
  3. wrapBuilder/@BuilderParam 传递时尽量传稳定引用或由父组件状态驱动重建;
  4. 如果跨 HSP/模块传递,避免把 PixelMap 当普通 DTO 长时间缓存,必要时改传 uri/ArrayBuffer 后在显示侧重新生成;
  5. 页面销毁、图片释放后不要继续复用旧 PixelMap。

这个问题信息有点少,不过按照经验看,PixelMap 本身有值,但通过 wrapBuilderImage 不显示,十有八九不是图片问题,而是 Builder 没刷新。

我一般先查这几个地方:

  • wrapBuilder 构建的时候,PixelMap 是不是还是 undefined
  • 后面拿到 PixelMap 后,是不是只改了变量,没有重新 update() 或重建 BuilderNode
  • PixelMap 有没有被提前 release()
  • 同一个 PixelMap 直接 Image(pixelMap) 能不能显示

可以先做个验证:

Image(this.pixelMap)

直接放页面里。

如果这里能显示,而 wrapBuilder 里的不显示,那基本就能确定是 wrapBuilder/BuilderNode 更新机制的问题,不是 PixelMap 本身的问题。

我自己遇到过几次,最后都是:

BuilderNode 创建时 PixelMap 还是空的,后面 PixelMap 有值了,但 Builder 没有刷新,所以 Image 一直不显示。

我倾向于先按“跨 HSP 传 PixelMap 本体”来排查。这个问题一般不是 Image 组件不能展示 PixelMap,官方 Image 组件的 src 类型就包含 PixelMap;更容易踩坑的是:PixelMap 背后有 native 图像内存和生命周期,不像字符串、数字这类普通 DTO,经过 wrapBuilder/@BuilderParam 再跨 HSP 传递时,引用、时序或资源有效性都可能变得不稳定。

建议把思路换成“传可重建的句柄,不传图像对象”:调用侧传 uri/path/ArrayBuffer,真正显示图片的 HSP 组件内部再 createImageSource -> createPixelMap,并用 @State 接住。这样组件第一次构建时没有图就显示占位,异步解码完成后状态刷新,Image 才会稳定重绘。

import { image } from '@kit.ImageKit';
import { fileIo as fs } from '@kit.CoreFileKit';

@Component
export struct HspImageView {
  srcUri: string = '';
  [@State](/user/State) pixelMap: image.PixelMap | undefined = undefined;

  async aboutToAppear(): Promise<void> {
    if (!this.srcUri) {
      return;
    }

    const file = fs.openSync(this.srcUri, fs.OpenMode.READ_ONLY);
    try {
      const source = image.createImageSource(file.fd);
      this.pixelMap = await source.createPixelMap();
    } finally {
      fs.closeSync(file.fd);
    }
  }

  build() {
    Stack() {
      if (this.pixelMap) {
        Image(this.pixelMap)
          .objectFit(ImageFit.Contain)
          .width('100%')
          .height('100%')
      } else {
        LoadingProgress()
      }
    }
    .width('100%')
    .height(200)
  }
}

如果一定要配合 wrapBuilder,可以让 Builder 参数只包含字符串等轻量字段:

class ImageArgs {
  srcUri: string = '';
}

@Builder
function HspImageBuilder($$: ImageArgs) {
  HspImageView({ srcUri: $$.srcUri })
}

// 调用侧只传 uri/path,不传 pixelMap 本体
// builderNode.build(wrapBuilder<[ImageArgs]>(HspImageBuilder), { srcUri: fileUri })

再补两个排查点:1)如果 srcUri 来自 Photo Picker 或文件管理器,要确认 HSP 侧仍有读取权限;临时 URI 建议先复制到应用沙箱再传。2)如果现在代码是异步拿到 pixelMap 后塞进普通局部变量,UI 不会自动刷新,要放到 @State/@Observed 等可观测状态里。

参考文档:Image 组件 https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-image ,PixelMap https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-image-pixelmap@BuilderParam https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-builderparam ,wrapBuilder https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-wrapbuilder

你好,可以考虑将 image.PixelMap 转为 base64 字符串作为参数在跨模块间传输,显示时再转为 pixelMap,实现PixelMap和base64的相互转换

async function pixelMap2Base64Str(pixelMap: image.PixelMap) : Promise<string> {
  const packingOpts: image.PackingOption = {
    quality: 100,
    format: 'image/png'
  };

  const imagePackerApi: image.ImagePacker = image.createImagePacker();
  const buf = await imagePackerApi.packToData(pixelMap, packingOpts);

  const base64Helper = new util.Base64Helper();
  const base64Str = base64Helper.encodeToStringSync(new Uint8Array(buf))
  return base64Str
}

问题原因

wrapBuilder 不是用来包装数据的,是用来包装自定义构建函数的。Image 组件直接接受 PixelMap 对象,不需要包装。

@State pixelMap: image.PixelMap | null = null;

build() {
    Column() {
        if (this.pixelMap) {
            // 直接使用,不要用 wrapBuilder
            Image(this.pixelMap)
                .width(200)
                .height(200)
        } else {
            Text('加载中...')
        }
    }
}

可以给出示例代码吗?比如hsp还是hap跨模块

PixelMap不展示通常因为:1. PixelMap的pixelFormat与Image组件要求不符,需转为RGBA_8888;2. wrapBuilder回调内未正确调用resolveDrawable或返回ImageSource。3. 跨线程传递时PixelMap被释放。确认PixelMap有效且调用位置在UI线程内。

您遇到的情况很可能是因为wrapBuilder传递参数时仅支持可序列化/可监听的基础类型,而PixelMap并不是这类数据。直接传递时组件无法感知数据变化或内部引用丢失,导致图片不展示。

常见处理方式:

  • 改用@State@Prop直接持有PixelMap变量,通过状态驱动Image更新。
  • 若必须通过wrapBuilder,可将PixelMap预先转换为ArrayBuffer或Base64字符串传递,再在目标组件内解码创建PixelMap
  • 也可尝试将PixelMap存入全局单例或LocalStorage中,通过key间接传递。

总结:wrapBuilder的传参机制不支持PixelMap这类复杂非序列化对象,调整数据传递方式即可解决。

回到顶部