如何实现文本在标签右侧显示并自动换行
==================

#### 问题现象

如何实现文本标题显示在标签右侧，当文本标题过长时自动换行至标签下方？需求效果如下：

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/6d/v3/LKIzYLv_R6yHDyQfo9krnA/zh-cn_image_0000002658792005.png?HW-CC-KV=V1&HW-CC-Date=20260709T134109Z&HW-CC-Expire=86400&HW-CC-Sign=351E26DE0748361AF585D657458F016BEC5995C4B7EA277346BBD367AC0FD3B0)

#### 背景知识

直接使用Text包含Span，无法设置Span相关样式，因此使用[CustomSpan](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-styled-string#customspan)自定义绘制Span。

*   自定义绘制Span时，使用画刷对象[Brush](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-graphics-drawing-brush)的[setColor](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-graphics-drawing-brush#setcolor)方法设置文本和标签颜色。
*   使用[TextBlob](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-graphics-drawing-textblob)的[makeFromPosText](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-graphics-drawing-textblob#makefrompostext12)方法绘制标签字形。
*   使用[Canvas](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-graphics-drawing-canvas)的[drawRoundRect](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-graphics-drawing-canvas#drawroundrect12)方法绘制圆角矩形，以此作为标签的背景。
*   通过属性字符串中[insertStyledString](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-styled-string#insertstyledstring)方法将标签插入初始化标题文本前。

#### 解决方案

通过CustomSpan在Text中绘制带圆角背景和文字的标签，借助MutableStyledString实现灵活插入，具体步骤如下：

1.  构建并绑定TextController。
    
    textController: TextController = new TextController();
    
    build() {
      Row() {
        Column() {
          Text(undefined, { controller: this.textController })
            .copyOption(CopyOptions.InApp)
            .fontSize(30);
        }
        .width('100%');
      }
      .height('100%')
      .padding(3)
      .expandSafeArea(\[SafeAreaType.SYSTEM\], \[SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM\]);
    }
    
2.  通过[MutableStyledString](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-styled-string#mutablestyledstring)加载初始化文本。
    
    style: MutableStyledString = new MutableStyledString(this.str, \[
      {
        start: 0,
        length: -1,
        styledKey: StyledStringKey.FONT,
        styledValue: new TextStyle({ fontColor: Color.Black })
      }
    \]);
    
3.  在页面组件的onPageShow生命周期中将预设的标签插入原始文本中并渲染。调用setStyledString()更新TextController，触发UI重绘。
    
    async onPageShow() {
      if (!this.isPageShow) {
        return;
      }
      this.isPageShow = false;
    
      for (let index = 0; index < this.tagsArr.length; index++) {
        let custom = this.tagsArr\[index\];
        this.style.insertStyledString(index, new StyledString(custom));
      }
      this.textController.setStyledString(this.style);
    }
    
4.  当TextController渲染文本时，系统会遍历每一个StyledString，遇到CustomSpan就调用其onDraw方法。
    
    onDraw(context: DrawContext, options: CustomSpanDrawInfo) {
     _ // 创建画布和画刷_
      let canvas = context.canvas;
      const brush = new drawing.Brush();
     _ // 定义画刷颜色，用于设置背景色_
      brush.setColor({
        alpha: 255,
        red: 229,
        green: 237,
        blue: 254
      });
     _ // 创建文字，设置文字属性_
      const font = new drawing.Font();
      font.setSize(gUIContext.vp2px(18));
      const textBlob = drawing.TextBlob.makeFromString(this.word, font, drawing.TextEncoding.TEXT\_ENCODING\_UTF8);
      _// 绑定画刷_
      canvas.attachBrush(brush);
     _ // 绘制Tag背景形状为圆角矩形_
      let rect: common2D.Rect = {
        left: options.x + 10,
        right: options.x + gUIContext.vp2px(this.width) - 10,
        top: options.lineTop + 10,
        bottom: options.lineBottom
      };
      let roundRect = new drawing.RoundRect(rect, gUIContext.vp2px(8), gUIContext.vp2px(8));
      canvas.drawRoundRect(roundRect);
    
      _// 定义画刷颜色，用于设置文本颜色_
      brush.setColor({
        alpha: 255,
        red: 12,
        green: 90,
        blue: 247
      });
      canvas.attachBrush(brush);
      _// 绘制文字_
      canvas.drawTextBlob(textBlob, options.x + gUIContext.vp2px(12), options.lineBottom - gUIContext.vp2px(this.height));
      canvas.detachBrush();
    }
    

完整代码如下：

import { common2D, drawing } from '@kit.ArkGraphics2D';

let gUIContext: UIContext;

class MyCustomSpan extends CustomSpan {
  constructor(word: string, width: number, height: number) {
    super();
    this.word = word;
    this.width = width;
    this.height = height;
  }

  onMeasure(): CustomSpanMetrics {
    return { width: this.width, height: this.height };
  }

  onDraw(context: DrawContext, options: CustomSpanDrawInfo) {
   _ // 创建画布和画刷_
    let canvas = context.canvas;
    const brush = new drawing.Brush();
  _  // 定义画刷颜色，用于设置背景色_
    brush.setColor({
      alpha: 255,
      red: 229,
      green: 237,
      blue: 254
    });
   _ // 创建文字，设置文字属性_
    const font = new drawing.Font();
    font.setSize(gUIContext.vp2px(18));
    const textBlob = drawing.TextBlob.makeFromString(this.word, font, drawing.TextEncoding.TEXT\_ENCODING\_UTF8);
   _ // 绑定画刷_
    canvas.attachBrush(brush);
   _ // 绘制Tag背景形状为圆角矩形_
    let rect: common2D.Rect = {
      left: options.x + 10,
      right: options.x + gUIContext.vp2px(this.width) - 10,
      top: options.lineTop + 10,
      bottom: options.lineBottom
    };
    let roundRect = new drawing.RoundRect(rect, gUIContext.vp2px(8), gUIContext.vp2px(8));
    canvas.drawRoundRect(roundRect);

  _  // 定义画刷颜色，用于设置文本颜色_
    brush.setColor({
      alpha: 255,
      red: 12,
      green: 90,
      blue: 247
    });
    canvas.attachBrush(brush);
   _ // 绘制文字_
    canvas.drawTextBlob(textBlob, options.x + gUIContext.vp2px(12), options.lineBottom - gUIContext.vp2px(this.height));
    canvas.detachBrush();
  }

  setWord(word: string) {
    this.word = word;
  }

  width: number = 160;
  word: string = 'drawing';
  height: number = 10;
}

@Entry
@Component
struct styled\_string\_set\_customspan\_demo {
  isPageShow: boolean = true;
  tagsArr: MyCustomSpan\[\] = \[\];
  @State str: string = '文本绘制 示例代码 CustomSpan';
  style: MutableStyledString = new MutableStyledString(this.str, \[
    {
      start: 0,
      length: -1,
      styledKey: StyledStringKey.FONT,
      styledValue: new TextStyle({ fontColor: Color.Black })
    }
  \]);

  aboutToAppear() {
    gUIContext = this.getUIContext();
    for (let index = 1; index < 5; index++) {
      this.tagsArr.push(new MyCustomSpan(\`tag${index}\`, 60, 8));
    }
  }

  async onPageShow() {
    if (!this.isPageShow) {
      return;
    }
    this.isPageShow = false;

    for (let index = 0; index < this.tagsArr.length; index++) {
      let custom = this.tagsArr\[index\];
      this.style.insertStyledString(index, new StyledString(custom));
    }
    this.textController.setStyledString(this.style);
  }

  textController: TextController = new TextController();

  build() {
    Row() {
      Column() {
        Text(undefined, { controller: this.textController })
          .copyOption(CopyOptions.InApp)
          .fontSize(30);
      }
      .width('100%');
    }
    .height('100%')
    .padding(3)
    .expandSafeArea(\[SafeAreaType.SYSTEM\], \[SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM\]);
  }

}

效果如下：

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/ab/v3/iV6wJaBNQKOTTCp5cf_2vg/zh-cn_image_0000002628552630.png?HW-CC-KV=V1&HW-CC-Date=20260709T134109Z&HW-CC-Expire=86400&HW-CC-Sign=92299F9C2840EC67A1F2682C778FCF3A81988221BF9379C8087E4CADBD7068A0 "点击放大")