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 + TextSpan:TextSpan 的样式选项和交互能力确实有限。
  • 多个 Text 组件行内排列:布局代码冗余,样式维护困难。

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

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

回到顶部