行内文本高亮样式设置方案
============

#### 问题现象

在对文本进行编辑时有些文字需要高亮显示，如搜索框内搜索内容、关键词提示、歌词播放等场景，如何实现该功能呢？

#### 效果预览

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/46/v3/L4CYRMTpTHiDK4e54tBLHg/zh-cn_image_0000002628399638.png?HW-CC-KV=V1&HW-CC-Date=20260709T133922Z&HW-CC-Expire=86400&HW-CC-Sign=810E1CFDD51574F268BF4F4D5B69453F66565C6A6CABE989C604118C32224982 "点击放大")

#### 背景知识

在开发文本展示和编辑功能时，我们可以使用几个关键组件、接口来增强用户体验。以下是这些组件和接口的功能概述及其使用方式：

*   [Span](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-span)组件是[Text](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-text)子组件，主要用于显示行内文本，并允许为不同的文字部分设置不同的属性，如颜色、字体大小等，从而实现高亮或其他视觉效果。
*   [AttributeModifier](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-user-defined-extension-attributemodifier)是属性修改器接口，提供了applyXxxAttribute方法，让开发者能够根据具体需求调整文本属性。
*   [MutableStyledString](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-styled-string#mutablestyledstring)是方便灵活应用文本样式的对象。可以通过[TextController](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-text#textcontroller11)中的[setStyledString](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-text#setstyledstring12)方法与Text组件绑定，实现动态地改变文本样式。
*   [RichEditor](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-richeditor)组件支持图文混排和文本交互编辑，可以实时更新文本样式，实现搜索匹配内容的高亮样式。适合需要编辑的场景，其他场景不推荐。

#### 解决方案

行内部分文本高亮样式设置可以通过Span组件、AttributeModifier属性修改器、MutableStyledString属性字符串、RichEditor组件等实现。

*   Span组件和AttributeModifier属性修改器方案主要是基于Text组件嵌套Span子组件，通过Span组件对需要高亮显示的文本单独设置高亮样式。
*   MutableStyledString属性字符串可以为指定范围的文字设置指定样式，Text组件和RichEditor组件均可通过setStyledString方法设置MutableStyledString属性。
*   RichEditor组件可以通过控制器[RichEditorController](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-richeditor#richeditorcontroller)的[addTextSpan](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-richeditor#addtextspan)方法添加文本内容，并设置文本内容的样式。

具体有以下四个实现方案：

*   **方案一**：Text组件将匹配的字符串通过Span设置高亮样式。
    
    @Entry
    @Component
    struct OptionOne {
      searchValue: string \= '';
      dataArr: string\[\] \= \[
        '南窗枕书卧，醉眼看山横',
        '海棠花下思朦胧，醉春风',
        '醉里挑灯看剑，梦回吹角连营',
        '醉后不知天在水，满船清梦压星河',
        '指洞庭为酒，渴时浩饮，君山作枕，醉后高眠'
      \];
      @State dataArr2: string\[\] \= \[\];
    
      build() {
        Row() {
          Column() {
            Search({ value: $$this.searchValue, placeholder: 'Type to search...' })
              .width('90%')
              .onChange(() \=\> {
                let dataArr3: string\[\] \= \[\];
                this.dataArr.filter((item: string) \=\> {
                  let index \= item.indexOf(this.searchValue);
                  if (this.searchValue && index != -1) {
                    dataArr3.push(item);
                  }
                });
                this.dataArr2 \= dataArr3;
              });
            Column() {
              ForEach(this.dataArr2, (item: string) \=\> {
                Row() {
                _  // 多关键字匹配_
                  Text() {
                    Span(item.substring(0, item.indexOf(this.searchValue)));
               _     // 青色高亮文字_
                    Span(this.searchValue).fontColor('#87ceeb');
                    Span(item.substring(item.indexOf(this.searchValue) \+ this.searchValue.length, item.length \- 1));
                _    // 最后一个文字显示为蓝色_
                    Span(item.substring(item.length \- 1)).fontColor(Color.Blue);
                  };
                }
                .width('100%')
                .height(40)
                .justifyContent(FlexAlign.Center);
              });
            };
          }
          .width('100%');
        }
        .height('100%');
      }
    }
    
*   **方案二**：AttributeModifier属性修改器通过[applyNormalAttribute](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-attributes-attribute-modifier#applynormalattribute)方法对Span类型组件回调，获取其属性对象并设置高亮样式。
    
    @Entry
    @Component
    struct OptionTwo {
      searchValue: string \= '';
      dataArr: string\[\] \= \[
        '南窗枕书卧，醉眼看山横',
        '海棠花下思朦胧，醉春风',
        '醉里挑灯看剑，梦回吹角连营',
        '醉后不知天在水，满船清梦压星河',
        '指洞庭为酒，渴时浩饮，君山作枕，醉后高眠'
      \];
      @State dataArr2: string\[\] \= \[\];
    
      build() {
        Row() {
          Column() {
            Search({ value: $$this.searchValue, placeholder: 'Type to search...' })
              .width('90%')
              .onChange(() \=\> {
                let dataArr3: string\[\] \= \[\];
                this.dataArr.filter((item: string) \=\> {
                  let index \= item.indexOf(this.searchValue);
                  if (this.searchValue && index != -1) {
                    dataArr3.push(item);
                  }
                });
                this.dataArr2 \= dataArr3;
              });
            Column() {
              ForEach(this.dataArr2, (item: string) \=\> {
                Row() {
               _   // 多关键字匹配_
                  Text() {
                    Span(item.substring(0, item.indexOf(this.searchValue))).attributeModifier(new HighlightModifier(false));
                _    // 青色高亮文字_
                    Span(this.searchValue).attributeModifier(new HighlightModifier(true));
                    Span(item.substring(item.indexOf(this.searchValue) \+ this.searchValue.length, item.length \- 1))
                      .attributeModifier(new HighlightModifier(false));
                _    // 最后一个文字显示为蓝色_
                    Span(item.substring(item.length \- 1))
                      .fontColor(Color.Blue);
                  };
                }
                .width('100%')
                .height(40)
                .justifyContent(FlexAlign.Center);
              });
            };
          }
          .width('100%');
        }
        .height('100%');
      }
    }
    
    export class HighlightModifier implements AttributeModifier<SpanAttribute\> {
      isHighlight: boolean \= false;
    
      constructor(isHighlight: boolean) {
        this.isHighlight \= isHighlight;
      }
    
      applyNormalAttribute(instance: SpanAttribute): void {
        instance.fontColor(this.isHighlight ? '#87ceeb' : Color.Black).fontSize(15);
      }
    }
    
*   **方案三**：使用属性字符串MutableStyledString给字符串添加高亮样式，然后通过Text组件的setStyledString方法设置其展示。
    
    import { cryptoFramework } from '@kit.CryptoArchitectureKit';
    
    @Entry
    @Component
    struct OptionFour {
      value: string \= '';
      dataArr: string\[\] \= \[
        '南窗枕书卧，醉眼看山横',
        '海棠花下思朦胧，醉春风',
        '醉里挑灯看剑，梦回吹角连营',
        '醉后不知天在水，满船清梦压星河',
        '指洞庭为酒，渴时浩饮，君山作枕，醉后高眠'
      \];
      @State showArr: TmpFour\[\] \= \[\];
      Style1: TextStyle \= new TextStyle({ fontColor: '#87ceeb' });
      Style2: TextStyle \= new TextStyle({ fontColor: Color.Blue });
    
      build() {
        Row() {
          Column() {
            Search({ value: $$this.value, placeholder: 'Type to search...' })
              .width('90%')
              .onChange(() \=\> {
                this.showArr \= \[\];
                this.dataArr.filter((item: string) \=\> {
                  let index \= item.indexOf(this.value);
             _     // 匹配关键词，并设置对应Style样式_
                  if (this.value && index != -1) {
                    let mutableStyledString \= new MutableStyledString(item, \[
                      {
                        start: index,
                        length: this.value.length,
                        styledKey: StyledStringKey.FONT,
                        styledValue: this.Style1
                      },
                      {
                        start: item.length \- 1,
                        length: 1,
                        styledKey: StyledStringKey.FONT,
                        styledValue: this.Style2
                      }\]);
                    this.showArr.push(new TmpFour(mutableStyledString, new TextController()));
                  }
                });
              });
            ForEach(this.showArr, (item: TmpFour) \=\> {
              Row() {
                Text(undefined, { controller: item.controller })
                  .onAppear(() \=\> {
              _      // 通过setStyledString方法，添加对应文本样式_
                    item.controller.setStyledString(item.style);
                  });
              }.width('100%').height(40).justifyContent(FlexAlign.Center);
            }, (item: TmpFour, index: number) \=\> {
              return index \+ '\_\_' \+ item.style.getString() \+ JSON.stringify(cryptoFramework.createRandom().generateRandom(10));
            });
          }
          .width('100%')
          .height('100%')
          .padding(16)
          .justifyContent(FlexAlign.Center);
        };
      }
    }
    
    class TmpFour {
      style: MutableStyledString;
      controller: TextController;
    
      constructor(style: MutableStyledString, controller: TextController) {
        this.style \= style;
        this.controller \= controller;
      }
    }
    
*   **方案四**：RichEditor组件获取匹配的字符串列表，然后通过addTextSpan方法对已匹配文本设置高亮样式。
    
    class TmpThree {
      controller: RichEditorController;
      value: string;
    
      constructor(controller: RichEditorController, value: string) {
        this.controller \= controller;
        this.value \= value;
      }
    }
    
    @Entry
    @Component
    struct OptionThree {
      searchValue: string \= '';
      dataArr: string\[\] \= \[
        '南窗枕书卧，醉眼看山横',
        '海棠花下思朦胧，醉春风',
        '醉里挑灯看剑，梦回吹角连营',
        '醉后不知天在水，满船清梦压星河',
        '指洞庭为酒，渴时浩饮，君山作枕，醉后高眠'
      \];
      @State dataArr2: TmpThree\[\] \= \[\];
    
     _ // 设置空键盘_
      @Builder
      TestBoard() {
      }
    
      build() {
        Row() {
          Column() {
            Search({ value: $$this.searchValue, placeholder: 'Type to search...' })
              .width('90%')
              .onChange(() \=\> {
                let dataArr3: TmpThree\[\] \= \[\];
                this.dataArr.filter((item: string) \=\> {
                  let index \= item.indexOf(this.searchValue);
                  if (this.searchValue && index != -1) {
                    let controller: RichEditorController \= new RichEditorController();
                    let temp: TmpThree \= new TmpThree(controller, item);
                    dataArr3.push(temp);
                  }
                });
                this.dataArr2 \= dataArr3;
              });
            ForEach(this.dataArr2, (item: TmpThree) \=\> {
              Row() {
                RichEditor({ controller: item.controller })
                  .customKeyboard(this.TestBoard()) _// 绑定空键盘，禁止编辑。_
                  .onReady(() \=\> {
                _    // 添加高亮显示的属性_
                    item.controller.addTextSpan(item.value.substring(0, item.value.indexOf(this.searchValue)),
                      {
                        style: { fontColor: Color.Black, fontSize: 16 }
                      });
                    item.controller.addTextSpan(this.searchValue,
                      {
                        style: { fontColor: '#87ceeb', fontSize: 16 }
                      });
                    item.controller.addTextSpan(item.value.substring(item.value.indexOf(this.searchValue) +
                    this.searchValue.length,
                      item.value.length \- 1),
                      {
                        style: { fontColor: Color.Black, fontSize: 16 }
                      });
                    item.controller.addTextSpan(item.value.substring(item.value.length \- 1),
                      {
                        style:
                        {
                          fontColor: Color.Blue,
                          fontSize: 16
                        }
                      });
                  })
              }.width('100%').height(40).justifyContent(FlexAlign.Center);
            });
          }
          .width('100%');
        }
        .height('100%');
      }
    }