HarmonyOS鸿蒙Next中属性字符串下的表达:打造高复用可定制化的协议弹窗

HarmonyOS鸿蒙Next中属性字符串下的表达:打造高复用可定制化的协议弹窗 尝试过使用Text组件进行拼接或者Text包裹TextSpan进行拼接,要么繁杂要么就是TextSpan的样式有限制,做不到完全定制化,有没有什么其它的方法实现这类需求场景?

3 回复

属性字符串

  • 方便灵活应用文本样式的对象,可通过TextController中的setStyledString方法与Text组件绑定,可通过RichEditorStyledStringController中的setStyledString方法与RichEditor组件绑定。

说明

从API version 12开始支持。

属性字符串目前不支持在worker线程中使用。

属性字符串通过controller绑定时,需要等待布局完成后,绑定生效。当measure和setStyledString同时使用,开发者需要通过@ohos.arkui.inspector (布局回调)判断布局完成,再绑定属性字符串。

cke_1219.jpeg

先来看下真机效果:

cke_6534.gif

前置条件:

在模块的module.json5文件中加上网络访问权限:以此满足协议的正常跳转

"requestPermissions": [
   {
     "name": "ohos.permission.INTERNET"
   }
],

实现步骤:

1、自定义弹窗组件;

2、使用mutableStyledString给字符串自定义各种样式以及超链接;

3、使用Text()组件加载属性字符串。

注意:本案例仅作功能实现参考,具体封装细节请自己结合项目封装实现

完整代码:

@CustomDialog
export struct PrivacyDialog {
  private control?: CustomDialogController
  url1: UrlStyle =
    new UrlStyle("https://term.developer.huaweicloud.com/rest/developer/agreementservice/v1/developer/agreement/agreement-template?agr_type=90109&language=zh_cn&version=2025111113&region=cn");
  url2: UrlStyle = new UrlStyle("https://developer.huawei.com/consumer/cn/devservice/use");
  privacyStr = "欢迎使用 XXAPP!为了向您提供更优质的服务,我们需要获取以下权限:点击同意及表示您同意《生态隐私协议》和《开发者联盟网站使用条款》"
  mutableStyledString: MutableStyledString =
    new MutableStyledString(this.privacyStr);
  textController: TextController = new TextController();

  aboutToAppear(): void {
    //匹配筛选出《》所包裹的超协议文本的字符串起始位置及长度
    const urlRouterArr = matchBracketContent(this.privacyStr)
    const urls = [this.url1, this.url2]
    urlRouterArr.forEach((item, index) => {
      //给特定文本加上超链接
      this.mutableStyledString.setStyle({
        start: item.start,
        length: item.length,
        styledKey: StyledStringKey.URL,
        styledValue: urls[index],

      })
      //给超链接添加文本颜色
      this.mutableStyledString.setStyle({
        start: item.start,
        length: item.length,
        styledKey: StyledStringKey.FONT,
        styledValue: new TextStyle({ fontColor: '#9980ff' })
      })
    })

    this.textController.setStyledString(this.mutableStyledString);
  }

  build() {
    Column() {
      Text(undefined, { controller: this.textController }).key('mutableStyledString').fontSize(20)
        .margin(20)

      Divider().backgroundColor(Color.Gray)
      Row() {
        Text("不同意并退出")
          .dialogBtn()
        Text("同意")
          .dialogBtn()
          .borderRadius({ bottomRight: 15 })
      }.borderRadius(15)
    }
    .borderRadius(15)
    .width("100%")
  }
}

@Extend(Text)
function dialogBtn() {
  .layoutWeight(1)
  .textAlign(TextAlign.Center)
  .padding(20)
  .fontSize(18)
}

export interface urlRouter {
  start: number;
  length: number;
}

/**
 * 匹配字符串中所有《》包裹内容的起始位置和总长度
 * @param str - 待匹配的原始字符串
 * @returns 匹配结果数组,每个元素包含起始位置和长度信息
 */
function matchBracketContent(str: string): Array<urlRouter> {
  const result: Array<urlRouter> = [];
  const reg = /《[^》]+》/g;
  let match: RegExpExecArray | null;
  while ((match = reg.exec(str)) !== null) {
    const fullContent = match[0];
    const start = match.index;
    const length = fullContent.length;
    result.push({ start, length });
  }
  return result;
}

弹窗组件使用:

import { PrivacyDialog } from 'xx'

@Entry
@Component
struct Index {
  dialogController: CustomDialogController = new CustomDialogController({
    builder: PrivacyDialog()
  })
  build() {
    Column() {
      Button("唤起隐私弹窗").onClick(()=>{
        this.dialogController.open()
      })
    }.justifyContent(FlexAlign.Center)
    .height('100%')
    .width('100%')
  }
}

StyleOptions对象说明

名称 类型 只读 可选 说明
start number 设置属性字符串样式的开始位置。当start的值小于0或超出字符串长度时,按0处理。
length number 设置属性字符串样式的长度。当length的值小于0或超出字符串长度与start的差值时,按字符串长度与start的差值处理。
styledKey StyledStringKey 样式类型的枚举值。
styledValue StyledStringValue 样式对象。

StyledStringValue

类型 说明
TextStyle 文本字体样式。
DecorationStyle 文本装饰线样式。
BaselineOffsetStyle 文本基线偏移量样式。
LetterSpacingStyle 文本字符间距样式。
LineHeightStyle 文本行高样式。
TextShadowStyle 文本阴影样式。
GestureStyle 事件手势样式。
ParagraphStyle 文本段落样式。
ImageAttachment 图片样式。
CustomSpan 自定义绘制Span样式。
UserDataSpan UserDataSpan样式。
UrlStyle 超链接样式。
BackgroundColorStyle 文本背景颜色样式。

相关文档:

更多关于HarmonyOS鸿蒙Next中属性字符串下的表达:打造高复用可定制化的协议弹窗的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS Next中,属性字符串(AttributeString)支持通过富文本标记实现动态内容与样式定制。开发者可定义包含占位符和样式的字符串模板,运行时动态替换占位符内容,并应用预设样式(如颜色、字体)。这适用于协议弹窗等场景,通过分离文本内容与样式逻辑,提升UI组件的复用性和定制灵活性。

在HarmonyOS Next中,实现高复用、可定制化的协议弹窗文本,推荐使用Span组件进行富文本拼接,这是目前最灵活的方式。

核心方案:使用 Span 组件 Span 提供了 SpanStyle 来精细控制每个片段的样式(字体、颜色、装饰线等),并通过 onClick 事件为特定片段添加点击交互,完美契合协议弹窗中“用户协议”、“隐私政策”等可点击文本的需求。

示例代码:

// 构建富文本片段
let mySpans: Span[] = [
  {
    content: '请仔细阅读并同意',
    style: SpanStyle.create({ fontSize: 16, fontColor: Color.Black })
  },
  {
    content: '《用户协议》',
    style: SpanStyle.create({ fontSize: 16, fontColor: Color.Blue, decoration: { type: TextDecorationType.Underline } }),
    onClick: () => { /* 跳转用户协议 */ }
  },
  {
    content: '和',
    style: SpanStyle.create({ fontSize: 16, fontColor: Color.Black })
  },
  {
    content: '《隐私政策》',
    style: SpanStyle.create({ fontSize: 16, fontColor: Color.Blue, decoration: { type: TextDecorationType.Underline } }),
    onClick: () => { /* 跳转隐私政策 */ }
  }
]

// 在Text组件中使用
Text() {
  Span() {
    ForEach(mySpans, (item: Span) => {
      Span(item.content)
        .spanStyle(item.style)
        .onClick(item.onClick) // 仅为可点击片段设置
    })
  }
}

关键优势:

  1. 样式完全解耦:每个文本片段的样式独立配置,支持字体、颜色、背景、装饰线、行高等。
  2. 交互精准绑定:可为任意片段单独设置点击事件,无需包装额外组件。
  3. 高性能复用:可将 Span 数组封装成自定义组件或通用函数,通过参数动态生成不同文案和样式的协议文本。

对比其他方式:

  • Text 拼接:无法为局部文本添加独立交互。
  • Text + TextSpanTextSpan 的样式选项和交互能力确实有限。
  • 多个 Text 组件行内排列:布局代码冗余,样式维护困难。

扩展建议: 对于更复杂的场景(如混合图标、自定义布局),可结合 FlexGrid 布局,将 SpanImage 等组件内联排列。若弹窗内容整体需要复用,建议将其封装为 @Builder 构建函数或自定义组件,通过回调函数处理点击事件。

这种方式在保持代码简洁的同时,提供了最强的定制化能力,是目前 HarmonyOS Next 中处理此类需求的最佳实践。

回到顶部