本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

一、作用

  • StyledString:属性字符串,用于在字符或段落级别上设置文本样式

  • MutableStyledString:可变的属性字符串,继承自StyledString

  • 功能:可以对文本进行精细化的样式控制,包括字体、颜色、大小、阴影、装饰线、行高、字符间距等

核心特性

  • 支持字符级和段落级的样式设置

  • 提供多种预定义样式对象

  • 支持自定义样式(CustomSpan)

  • 支持图文混排

  • 支持事件绑定(点击、长按)

  • 支持HTML格式转换

  • 支持动态更新样式

二、使用

2.1 创建和绑定

@Entry
@Component
struct styled_string_demo1 {
  // 创建StyledString
  styledString1: StyledString = new StyledString("上课45分钟");
  
  // 创建MutableStyledString
  mutableStyledString1: MutableStyledString = new MutableStyledString("下课15分钟");
  
  // 创建TextController
  controller1: TextController = new TextController();
  controller2: TextController = new TextController();
  
  // 在onPageShow中绑定
  async onPageShow() {
    this.controller1.setStyledString(this.styledString1);
  }
  
  build() {
    Column() {
      // 显示属性字符串
      Text(undefined, { controller: this.controller1 })
      
      Text(undefined, { controller: this.controller2 })
        .onAppear(() => {
          // 在组件onAppear回调中绑定
          this.controller2.setStyledString(this.mutableStyledString1);
        })
    }
    .width('100%')
  }
}

2.2 绑定时机

  • 推荐在onPageShowonAppear回调中绑定

  • API version 15之前,在aboutToAppear中绑定无法在页面初始化时显示

  • API version 15开始,aboutToAppear中也可以正常显示

三、样式类型

3.1 文本字体样式(TextStyle)

import { LengthMetrics } from '@kit.ArkUI';

const textStyleAttrs: TextStyle = new TextStyle({
  fontWeight: FontWeight.Bolder,
  fontSize: LengthMetrics.vp(24),
  fontStyle: FontStyle.Italic,
  strokeWidth: LengthMetrics.px(5),
  strokeColor: Color.Green,
  fontColor: Color.Orange,
  superscript: SuperscriptStyle.SUPERSCRIPT
});

3.2 文本阴影样式(TextShadowStyle)

const textShadowStyle = new TextShadowStyle({
  radius: 5,
  type: ShadowType.COLOR,
  color: Color.Red,
  offsetX: 10,
  offsetY: 10
});

3.3 文本装饰线样式(DecorationStyle)

const decorationStyle = new DecorationStyle({
  type: TextDecorationType.LineThrough, // 删除线
  color: Color.Red,
  thicknessScale: 3
});

// 支持多装饰线
const multiDecoration = new DecorationStyle(
  { type: TextDecorationType.Underline },
  { enableMultiType: true }  // 开启多装饰线
);

3.4 基线偏移样式(BaselineOffsetStyle)

import { LengthMetrics } from '@kit.ArkUI';

const baselineOffsetStyle = new BaselineOffsetStyle(LengthMetrics.px(20));

3.5 行高样式(LineHeightStyle)

import { LengthMetrics } from '@kit.ArkUI';

const lineHeightStyle = new LineHeightStyle(LengthMetrics.vp(50));

3.6 字符间距样式(LetterSpacingStyle)

import { LengthMetrics, LengthUnit } from '@kit.ArkUI';

const letterSpacingStyle = new LetterSpacingStyle(
  new LengthMetrics(20, LengthUnit.VP)
);

四、段落样式

4.1 段落分割

  • 段落以换行符\n结尾

  • ParagraphStyle可应用于段落开头、末尾或中间的任何位置

4.2 创建段落样式

import { LengthMetrics } from '@kit.ArkUI';

// 标题段落样式
const titleParagraphStyleAttr: ParagraphStyle = new ParagraphStyle({
  textAlign: TextAlign.Center
});

// 正文段落样式(首行缩进15vp)
const paragraphStyleAttr1: ParagraphStyle = new ParagraphStyle({
  textIndent: LengthMetrics.vp(15)
});

// 创建带段落样式的属性字符串
const paragraphStyledString1: MutableStyledString = new MutableStyledString(
  "段落标题\n正文第一段落开始0123456789正文第一段落结束。",
  [
    {
      start: 0,
      length: 4,
      styledKey: StyledStringKey.PARAGRAPH_STYLE,
      styledValue: titleParagraphStyleAttr
    },
    {
      start: 5,
      length: 20,
      styledKey: StyledStringKey.LINE_HEIGHT,
      styledValue: new LineHeightStyle(new LengthMetrics(24))
    }
  ]
);

4.3 动态更新段落样式

// 创建新样式
const paragraphStyleAttr3: ParagraphStyle = new ParagraphStyle({
  textAlign: TextAlign.End,
  maxLines: 1,
  wordBreak: WordBreak.BREAK_ALL,
  overflow: TextOverflow.Ellipsis
});

// 替换样式
this.paragraphStyledString1.replaceStyle({
  start: 5,
  length: 3,
  styledKey: StyledStringKey.PARAGRAPH_STYLE,
  styledValue: paragraphStyleAttr3
});

// 更新显示
this.controller.setStyledString(this.paragraphStyledString1);

五、自定义样式(CustomSpan)

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

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

  onMeasure(measureInfo: CustomSpanMeasureInfo): 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: 0, green: 74, blue: 175 });
    
    const font = new drawing.Font();
    font.setSize(25);
    
    const textBlob = drawing.TextBlob.makeFromString(
      this.word, font, drawing.TextEncoding.TEXT_ENCODING_UTF8
    );
    
    canvas.attachBrush(brush);
    canvas.drawRect({
      left: options.x + 10,
      right: options.x + this.context.vp2px(this.width) - 10,
      top: options.lineTop + 10,
      bottom: options.lineBottom - 10
    });
    
    brush.setColor({ alpha: 255, red: 23, green: 169, blue: 141 });
    canvas.attachBrush(brush);
    canvas.drawTextBlob(textBlob, options.x + 20, options.lineBottom - 15);
    canvas.detachBrush();
  }
}

六、图文混排

6.1 添加图片(ImageAttachment)

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

async aboutToAppear() {
  // 获取图片资源
  this.imagePixelMap = await this.getPixmapFromMedia($r('app.media.sea'));
}

private async getPixmapFromMedia(resource: Resource) {
  let unit8Array = await this.getUIContext().getHostContext()
    ?.resourceManager?.getMediaContent(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;
}

// 创建带图片的属性字符串
this.mutableStr = new MutableStyledString(new ImageAttachment({
  value: this.imagePixelMap,
  size: { width: 180, height: 160 },
  verticalAlign: ImageSpanAlignment.BASELINE,
  objectFit: ImageFit.Fill
}));

七、事件处理

7.1 手势事件样式(GestureStyle)

const gestureStyleAttr: GestureStyle = new GestureStyle({
  onClick: () => {
    this.backgroundColor1 = Color.Green;
  },
  onLongPress: () => {
    this.backgroundColor1 = Color.Grey;
  }
});

// 设置样式
this.customSpanStyledString.setStyle({
  start: 0,
  length: 1,
  styledKey: StyledStringKey.GESTURE,
  styledValue: this.gestureStyleAttr
});

7.2 动态更新样式

// 通过setStyle接口叠加新样式或更新已有样式
this.customSpanStyledString.setStyle({
  start: 0,
  length: 1,
  styledKey: StyledStringKey.GESTURE,
  styledValue: this.gestureStyleAttr
});

// 需要主动触发更新
this.textController.setStyledString(this.customSpanStyledString);

八、HTML格式转换

8.1 转换为HTML(toHtml)

// 将属性字符串转换为HTML格式
this.html = StyledString.toHtml(this.styledString);

8.2 从HTML转换(fromHtml)

// 从HTML格式转换回属性字符串
let styledString = await StyledString.fromHtml(this.html);
this.controller2.setStyledString(styledString);

8.3 支持的HTML标签

  • <p><span><img><br>

  • <strong><b><a><i><em>

  • <s><u><del><sup><sub>

8.4 HTML转换

@State html: string = "<p>This is <b>b</b> <strong>strong</strong> ...</p>";
@State spanString: StyledString | undefined = undefined;

Button("Converted HTML to SpanString")
  .onClick(async () => {
    this.spanString = await StyledString.fromHtml(this.html);
    this.controller.setStyledString(this.spanString);
  })

Button("Converted SpanString to HTML")
  .onClick(() => {
    if (this.spanString) {
      const newHtml = StyledString.toHtml(this.spanString);
      this.html = newHtml;
    }
  })

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐