HarmonyOS 如何生成海报并保存到相册?

将相关文字、图片在页面布局展示,该布局区域会超出范围,带有滚动条。希望将整个区域(包括超出范围的区域)生成图片并保存到相册。@ohos.arkui.componentSnapshot (组件截图) 当布局区域超出范围可滚动时,只能截到可视区域的页面。是否有其他方法可截取整个组件?

阅读 453
1 个回答

可以参考以下demo:

import { componentSnapshot, display } from '@kit.ArkUI'
import { image } from '@kit.ImageKit';
import { BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct ListSnapShot {
  private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
  listID: string = 'list';
  @State pixelMap: PixelMap | null = null;
  scroller: Scroller = new Scroller();
  bufferArr: ArrayBuffer[] = [];
  tempPixelMap: PixelMap | null = null;
  scrollOffsets: number[] = []; // 每屏List的起始位置
  densityPixels: number = 0; // 屏幕像素密度,是px单位和vp单位的比例关系,px = densityPixels * vp
  listWidth: number = 0;
  listHeight: number = 0;
  stride: number = 0;
  aboutToAppear(): void {
    let defaultDisplay = display.getDefaultDisplaySync();
    this.densityPixels = defaultDisplay.densityPixels;
  }
  getPixelMap() {
    let pixelOffset: number = 0;
    for (let i = 0; i < this.bufferArr.length; i++) {
      pixelOffset += this.scrollOffsets[i];
    }
    let opts: image.InitializationOptions = { editable: true, pixelFormat: 3, size: { width: this.listWidth, height: this.scrollOffsets[this.scrollOffsets.length - 1] * this.densityPixels + this.listHeight } };
    this.pixelMap = image.createPixelMapSync(opts);
    this.tempPixelMap = image.createPixelMapSync(opts);
    for (let i = 0; i < this.bufferArr.length; i++) {
      let area: image.PositionArea = {
        pixels: this.bufferArr[i],
        offset: 0,
        stride: this.stride,
        region: {
          size: {
            width: this.listWidth,
            height: this.listHeight
          },
          x: 0,
          y: this.scrollOffsets[i] * this.densityPixels
        }
      }
      this.tempPixelMap?.writePixels(area, (error: BusinessError) => {
        if (error) {
          console.log('error: ' + JSON.stringify(error));
        } else {
          console.log('no error');
        }
      })
    }
    this.pixelMap = this.tempPixelMap;
  }
  getPixelMapData() {
    //将List的当前滚动量记录下来
    let scrollOffset = this.scroller.currentOffset();
    this.scrollOffsets.push(scrollOffset.yOffset);
    componentSnapshot.get(this.listID, (error: Error, pixmap: PixelMap) => {
      if (this.listWidth == 0) {
        let imageInfo = pixmap.getImageInfoSync();
        this.listWidth = imageInfo.size.width;
        this.listHeight = imageInfo.size.height;
        this.stride = pixmap.getBytesNumberPerRow();
      }
      let bytesNumber = pixmap.getPixelBytesNumber();
      let buffer: ArrayBuffer = new ArrayBuffer(bytesNumber);
      pixmap.readPixelsToBufferSync(buffer);
      this.bufferArr.push(buffer);
      if (!this.scroller.isAtEnd()) {//滚动截图,如果没有滚到底,则继续滚到下一页
        this.scroller.scrollPage({ next: true });
        setTimeout(() => {//由于scrollPage存在延时,这里使用setTimeout,避免还未滚到下一页就又开始获取重复buffer
          this.getPixelMapData();
        }, 220)
      } else {//滚到底后,拿到之前获取的buffer,进行拼接生成长图
        this.getPixelMap();
      }
    })
  }
  build() {
    Column() {
      List({ space: 20, initialIndex: 0, scroller: this.scroller }) {
        ForEach(this.arr, (item: number) => {
          ListItem() {
            Text('' + item)
              .width('100%').height(100).fontSize(16)
              .textAlign(TextAlign.Center).borderRadius(10).backgroundColor(0xFFFFFF)
          }
        }, (item: string) => item)
      }
      .id(this.listID)
      .listDirection(Axis.Vertical) // 排列方向
      .scrollBar(BarState.Off)
      .friction(0.6)
      .divider({ strokeWidth: 2, color: 0xFFFFFF, startMargin: 20, endMargin: 20 }) // 每行之间的分界线
      .edgeEffect(EdgeEffect.Spring) // 边缘效果设置为Spring
      .width('90%')
      .height('50%')
      Button('截图')
        .onClick(() => {
          this.getPixelMapData();
        })
      Scroll() {
        Image(this.pixelMap)
          .width('80%')
          .objectFit(ImageFit.Contain)
      }
      .scrollable(ScrollDirection.Vertical)
      .height('40%')
    }
    .width('100%')
    .height('100%')
    .backgroundColor(0xDCDCDC)
    .padding({ top: 5 })
  }
}