场景描述

应用中基于原生能力实现图文混排效果有多个方案可实现,推荐使用Flex作父容器实现的方案(方案一),此方案优点在于节点数量少,结构简单。

方案一:基于Flex作父容器实现图文混排

建议基于Flex容器作为父容器实现图文混排,优点在于减少节点数量。

内部头像使用Image组件,中间部分使用Text文本组件,右边使用Text文本组件。

核心代码

Flex({ direction: FlexDirection.Row }) {
  Image($r('app.media.heard'))
    .width(30)
    .borderRadius(15)
  Text() {
    Span('文本')
      .fontSize(15)
    ImageSpan($r('app.media.member'))
      .width('40px')
      .height('40px')
      .objectFit(ImageFit.Fill)
      .verticalAlign(ImageSpanAlignment.BASELINE)
      .onClick(() => {
        console.log('测试输出')
      })
    Span('\n')
    Span('昨天 12:00')
      .fontSize(12)
      .fontColor('#ffcac8c8')
  }
  .margin({
    left: 10
  })

  Text('+关注')
    .fontSize('28px')
    .fontColor('#ff8200')
    .border({
      width: 1,
      color: '#ff8200',
      radius: 10,
      style: BorderStyle.Solid
    })
    .padding({
      left: 5,
      right: 5,
      top: 2,
      bottom: 2
    })
    .position({ x: 280, y: 2 })
}

方案二:基于RelativeContainer相对布局实现图文混排效果

使用此方案RelativeContainer子组件要用alignRules布局组件位置,布局时需指定容器id。参与相对布局的容器内组件必须设置id,不设置id的组件组件不显示,容器id固定为\_\_container\_\_。

如以下核心代码中子组件都设置id,父容器未设置id时,父容器默认id固定为\_\_container\_\_。

核心代码

// 相对布局
RelativeContainer() {
  Image($r('app.media.heard'))
    .width(30)
    .borderRadius(15)
    .id('Image')
      // 以容器作为锚点布局
    .alignRules({
      top: { anchor: '__container__', align: VerticalAlign.Top },
      left: { anchor: '__container__', align: HorizontalAlign.Start }
    })
  Text() {
    Span('文本')
      .fontSize(15)
    ImageSpan($r('app.media.member'))
      .width('40px')
      .height('40px')
      .objectFit(ImageFit.Fill)
      .verticalAlign(ImageSpanAlignment.BASELINE)
      .onClick(() => {
        console.log('测试输出')
      })
    Span('\n')
    Span('昨天 12:00')
      .fontSize(12)
      .fontColor('#ffcac8c8')
  }
  .id('Text1')
  // 以容器内子组件作为锚点进行布局
  .alignRules({
    top: { anchor: 'Image', align: VerticalAlign.Top },
    left: { anchor: 'Image', align: HorizontalAlign.End }
  })
  .margin({
    left: 10
  })

  Text('+关注')
    .fontSize('28px')
    .fontColor('#ff8200')
    .border({
      width: 1,
      color: '#ff8200',
      radius: 10,
      style: BorderStyle.Solid
    })
    .id('Text2')
      // 以容器内子组件作为锚点进行布局
    .alignRules({
      top: { anchor: 'Text1', align: VerticalAlign.Top },
      left: { anchor: 'Text1', align: HorizontalAlign.End },
    })
    .margin({
      left: 150
    })
    .padding({
      left: 5,
      right: 5,
      top: 2,
      bottom: 2
    })
}

方案三:基于线性布局实现图文混排效果

基于线性布局实现图文混排时,节点数量会比较多。

核心代码

Row() {
  Image($r('app.media.img1'))
    .width(30)
    .borderRadius(15)

  Column() {
    Row() {
      Text('文本')
        .fontSize(15)
      Image($r('app.media.vvip_1'))
        .width(30)
        .borderRadius(15)
        .margin({ left: 10 })
        .position({ x: 40, y: 2 })
    }

    Text('昨天 12:00')
      .fontSize(12)
      .fontColor('#ffcac8c8')
  }
  .margin({ left: 10, top: 2 })
  // 设置Column容器内子组件水平方向上布局
  .justifyContent(FlexAlign.Start)
  // 设置Column容器内子组件垂直方向上布局
  .alignItems(HorizontalAlign.Start)

  Text('+关注')
    .fontSize('28px')
    .fontColor('#ff8200')
    .border({
      width: 1,
      color: '#ff8200',
      radius: 10,
      style: BorderStyle.Solid
    })
    .padding({
      left: 5,
      right: 5,
      top: 2,
      bottom: 2
    })
    .position({ x: 280, y: 2 })
}
.width('100%')
.margin({ left: 10, top: 50 })

方案四:基于StyledString(属性字符串)实现图文混排效果

使用StyledString实现图文混排效果,首先得让Text组件与StyledString绑定,绑定后即可使用StyledString设置文本样式以及对文本进行增、删、改、查等操作。

StyledString是一个方便灵活应用文本样式的对象,Text组件可通过TextController中的setStyleString方法与属性字符串绑定。绑定之后即可通过StyledString对文本进行增、删、改、查等一系列操作,并且可以用StyledString设置文本样式。

注意:

  1. 组件样式和属性字符串样式冲突时,属性字符串优先级高,冲突样式以属性字符串设置样式为准。
  2. Text子组件样式与属性字符串样式冲突,以属性字符串为准。
  3. 属性字符串对象不支持@State修饰。

核心代码

import { image } from '@kit.ImageKit'
import { LengthMetrics, LengthMetricsUnit } from '@ohos.arkui.node';

@Component
export struct FourTh {
  // PixelMap图片
  imagePixelMap: image.PixelMap | undefined = undefined;
  // 属性字符串对象
  mutableStr: MutableStyledString = new MutableStyledString('');
  controller: TextController = new TextController();
  // 基于属性字符串设置文本样式
  fontStyle2: StyledStringValue = new TextStyle({
    fontColor: '#ffcac8c8',
    fontSize: LengthMetrics.vp(12)
  })

  async aboutToAppear() {
    // 获取图片资源并同步解析成PixelMap
    this.imagePixelMap = await this.getPixmapFromMedia($r('app.media.member'));
  }

  // 同步将资源解析成PixelMap
  private async getPixmapFromMedia(resource: Resource) {
    let unit8Array = await getContext(this)?.resourceManager?.getMediaContent({
      bundleName: resource.bundleName,
      moduleName: resource.moduleName,
      id: resource.id
    })
    let imageSource = image.createImageSource(unit8Array.buffer.slice(0, unit8Array.buffer.byteLength));
    let createPixelMap: image.PixelMap = await imageSource.createPixelMap({
      desiredPixelFormat: image.PixelMapFormat.RGBA_8888
    });
    await imageSource.release();
    return createPixelMap;
  }

  build() {
    NavDestination() {
      Row() {
        Image($r('app.media.heard'))
          .width(30)
          .borderRadius(15)
          .margin({
            right: 10,
            top: 5
          })
        // Text组件绑定controller
        Text(undefined, { controller: this.controller })
          .fontSize(15)
        Text('+关注')
          .fontSize('28px')
          .fontColor('#ff8200')
          .border({
            width: 1,
            color: '#ff8200',
            radius: 10,
            style: BorderStyle.Solid
          })
          .padding({
            left: 5,
            right: 5,
            top: 2,
            bottom: 2
          })
          .position({ x: 280, y: 2 })
      }
      .onAppear(() => {
        setTimeout(() => {
          if (this.imagePixelMap !== undefined) {
            // ImageAttachment: New图形对象并设置样式
            this.mutableStr = new MutableStyledString(new ImageAttachment({
              value: this.imagePixelMap,
              size: { width: '40px', height: '40px' },
              // layoutStyle: { borderRadius: LengthMetrics.vp(10) },
              verticalAlign: ImageSpanAlignment.BASELINE,
              objectFit: ImageFit.Fill
            }))
          }
          // insertString:在指定字符之前插入文本,此处即在图片之前添加文本
          this.mutableStr.insertString(0, '文本');
          // 在图片之后添加文本
          let str = new StyledString('\n昨天  12:00', [{
            start: 0,
            length: 10,
            styledKey: StyledStringKey.FONT,
            styledValue: this.fontStyle2
          }])
          // appendStyledString: 在末尾位置追加新的属性字符串
          this.mutableStr.appendStyledString(str);
          // 通过TextController中的setStyledString方法让属性字符串与Text组件绑定
          this.controller.setStyledString(this.mutableStr);
        }, 100)
      })
      .height(200)
      .margin({ left: 10, top: 50 })
      .width('100%')
      .alignItems(VerticalAlign.Top)
    }.title('方案4')
  }
}

HarmonyOS码上奇行
7k 声望2.8k 粉丝