目录

文本显示 (Text/Span)

Text是文本组件,用于展示用户视图,如显示文章的文字内容。该组件支持绑定自定义文本选择菜单,用户可根据需要选择不同功能。此外,还可以扩展自定义菜单,丰富可用选项,进一步提升用户体验。Span则用于展示行内文本。

具体用法请参考Text和Span组件的API文档。

常见问题请参考文本显示(Text/Span)常见问题。

创建文本

Text可通过以下两种方式来创建:

添加子组件

Span只能作为Text和RichEditor组件的子组件显示文本内容。可以在一个Text内添加多个Span来显示一段信息,例如产品说明书、承诺书等。

创建自定义文本样式

Text组件支持创建自定义文本样式,以下为修改文本样式的主要属性。

属性名称 功能描述
baselineOffset 设置文本基线的偏移量。
contentTransition 设置数字翻牌效果。
copyOption 设置文本是否可复制粘贴。
decoration 设置文本装饰线样式,如类型、颜色及其粗细。
enableAutoSpacing 设置是否开启中文与西文的自动间距。
enableDataDetector 设置是否进行文本特殊实体识别。
font 设置文本字体相关属性。
fontColor 设置文本字体颜色。
fontFamily 设置文本字体族。
fontFeature 设置文字特性效果,比如数字等宽的特性。
fontSize 设置文本字体大小。
fontStyle 设置文本字体风格。
fontWeight 设置文本字体粗细。
halfLeading 设置文本是否将行间距平分至行的顶部与底部。
heightAdaptivePolicy 设置文本自适应布局调整字号的方式。
letterSpacing 设置文本字符间距。
lineHeight 设置文本行高。
lineSpacing 设置文本的行间距。
marqueeOptions 设置跑马灯配置项,如开关、步长、循环次数、方向等。
maxFontSize 设置自适应字体最大尺寸。
maxLines 设置文本最大显示行数。
minFontSize 设置自适应字体最小尺寸。
optimizeTrailingSpace 控制每行末尾空格的优化。
privacySensitive 设置是否支持卡片敏感隐私信息。
shaderStyle 设置文本渐变色样式。
textCase 设置文本大小写转换。
textAlign 设置文本段落在水平方向的对齐方式。
textIndent 设置首行文本缩进。
textOverflow 控制文本超长处理方式。
textSelectable 设置文本是否可选择。
textVerticalAlign 设置文本段落在垂直方向的对齐方式。
wordBreak 设置断行规则。

下面对常用的接口进行举例说明。

添加事件

Text组件可以添加通用事件,可以绑定onClick、onTouch等事件来响应操作。

// xxx.ets
import { hilog } from '@kit.PerformanceAnalysisKit';
@Entry
@Component
struct GeneralEvents {
  @State textStr1: string = '';
  @State textStr2: string = '';

  build() {
    NavDestination() {
      Row() {
        Column() {
          Text('This is a text component.')
            .fontSize(30)
            .onClick(() => {
              hilog.info(0x0000, 'Sample_TextComponent', 'Text onClick is triggering');
              this.textStr1 = 'Text onClick is triggering';
            })
            .onTouch(() => {
              hilog.info(0x0000, 'Sample_TextComponent', 'Text onTouch is triggering');
              this.textStr2 = 'Text onTouch is triggering';
            })
          Text('onClick:' + this.textStr1)
            .fontSize(20)
          Text('onTouch:' + this.textStr2)
            .fontSize(20)
        }.width('100%')
      }
      .height('100%')
    }
    // ···
  }
}

设置垂直居中

从API version 20开始,Text组件支持通过textVerticalAlign属性实现文本段落在垂直方向的对齐。

设置选中菜单

[h2]弹出选中菜单

[h2]关闭选中菜单

使用Text组件时,若需要实现点击空白处关闭选中的场景,分为以下两种情况:

[h2]屏蔽系统菜单回调和自定义扩展菜单

从API version 12开始,支持通过editMenuOptions屏蔽系统菜单回调和自定义扩展菜单项。

// xxx.ets
@Entry
@Component
struct CustomAndBlockMenus {
  private static readonly CREATE_MENU_ITEM_ID_1: string = 'create1';
  private static readonly CREATE_MENU_ITEM_ID_2: string = 'create2';
  private static readonly PREPARE_MENU_ITEM_ID: string = 'prepare1';
  private controller: TextController = new TextController();
  @State private text: string = 'Text editMenuOptions';
  @State private endIndex: number = 0;
  @State blockCallbackText: string = '';

  // 创建菜单项辅助方法
  private createMenuItem(id: string, content: string): TextMenuItem {
    // $r('app.media.startIcon')需要替换为开发者所需的图像资源文件
    return {
      content: content,
      icon: $r('app.media.startIcon'),
      id: TextMenuItemId.of(id)
    };
  }

  // 查找菜单项索引
  private findMenuItemIndex(menuItems: Array<TextMenuItem>, menuItemId: TextMenuItemId): number {
    return menuItems.findIndex((item: TextMenuItem) => item.id.equals(menuItemId));
  }

  // 创建菜单回调
  private onCreateMenu = (menuItems: Array<TextMenuItem>): Array<TextMenuItem> => {
    const createItem1: TextMenuItem = this.createMenuItem(
      CustomAndBlockMenus.CREATE_MENU_ITEM_ID_1,
      'create1'
    );

    const createItem2: TextMenuItem = this.createMenuItem(
      CustomAndBlockMenus.CREATE_MENU_ITEM_ID_2,
      'create2'
    );

    // 添加自定义菜单项
    menuItems.push(createItem1);
    menuItems.unshift(createItem2);

    // 移除不需要的系统菜单项
    this.removeMenuItemById(menuItems, TextMenuItemId.AI_WRITER);
    this.removeMenuItemById(menuItems, TextMenuItemId.TRANSLATE);

    return menuItems;
  }

  // 移除指定菜单项
  private removeMenuItemById(menuItems: Array<TextMenuItem>, menuItemId: TextMenuItemId): void {
    const targetIndex: number = this.findMenuItemIndex(menuItems, menuItemId);
    if (targetIndex !== -1) {
      menuItems.splice(targetIndex, 1);
    }
  }

  // 菜单项点击回调
  private onMenuItemClick = (menuItem: TextMenuItem, textRange: TextRange): boolean => {
    const menuItemId: TextMenuItemId = menuItem.id;

    // 处理自定义菜单项,return false,点击自定义菜单项后菜单会关闭
    if (menuItemId.equals(TextMenuItemId.of(CustomAndBlockMenus.CREATE_MENU_ITEM_ID_2))) {
      let msg = '拦截 id: create2 start:' + textRange.start + '; end:' + textRange.end;
      this.blockCallbackText = msg;
      return false;
    }
    // 处理自定义菜单项,return true,点击自定义菜单项后菜单不会关闭
    if (menuItemId.equals(TextMenuItemId.of(CustomAndBlockMenus.PREPARE_MENU_ITEM_ID))) {
      let msg = '拦截 id: prepare1 start:' + textRange.start + '; end:+' + textRange.end;
      this.blockCallbackText = msg;
      return true;
    }

    // 处理系统菜单项,return true,拦截系统默认逻辑,此时点击复制菜单不会关闭
    if (menuItemId.equals(TextMenuItemId.COPY)) {
      let msg = '拦截 COPY start:' + textRange.start + '; end:' + textRange.end;
      this.blockCallbackText = msg;
      // 可以通过文本控制器关闭菜单,手柄也会消失,仅保持选中区域,点击可消失
      this.controller.closeSelectionMenu();
      return true;
    }
    // 处理系统菜单项,return false,不拦截系统默认逻辑,自定义逻辑亦会被执行
    if (menuItemId.equals(TextMenuItemId.SELECT_ALL)) {
      let msg = '不拦截 SELECT_ALL start:' + textRange.start + '; end:' + textRange.end;
      this.blockCallbackText = msg;
      return false;
    }

    return false;
  }
  // 准备菜单回调
  private onPrepareMenu = (menuItems: Array<TextMenuItem>): Array<TextMenuItem> => {
    const prepareItem: TextMenuItem = this.createMenuItem(
      CustomAndBlockMenus.PREPARE_MENU_ITEM_ID,
      `prepare1_${this.endIndex}`
    );

    menuItems.unshift(prepareItem);
    return menuItems;
  }
  // 编辑菜单选项
  @State private editMenuOptions: EditMenuOptions = {
    onCreateMenu: this.onCreateMenu,
    onMenuItemClick: this.onMenuItemClick,
    onPrepareMenu: this.onPrepareMenu
  };
  // 文本选择变化回调
  private onTextSelectionChange = (selectionStart: number, selectionEnd: number): void => {
    this.endIndex = selectionEnd;
  }

  build() {
    NavDestination() {
      Column() {
        Text(this.text, { controller: this.controller })
          .fontSize(20)
          .copyOption(CopyOptions.LocalDevice)
          .editMenuOptions(this.editMenuOptions)
          .margin({ top: 100 })
          .onTextSelectionChange(this.onTextSelectionChange)
        Text(this.blockCallbackText).borderWidth(1)
      }
      .width('90%')
      .margin('5%')
    }
  }
}

[h2]屏蔽系统服务类菜单

[h2]默认菜单支持自定义刷新能力

从API version 20开始,当文本选择区域变化后显示菜单之前触发onPrepareMenu回调,可在该回调中进行菜单数据设置。

// 请将$r('app.media.xxx')替换为实际资源文件
// xxx.ets
import { hilog } from '@kit.PerformanceAnalysisKit';
const DOMAIN = 0x0000;
@Entry
@Component
struct PrepareMenu {
  @State text: string = 'Text editMenuOptions';
  @State endIndex: number = 0;
  onCreateMenu = (menuItems: Array<TextMenuItem>) => {
    let item1: TextMenuItem = {
      content: 'create1',
      icon: $r('app.media.startIcon'),
      id: TextMenuItemId.of('create1'),
    };
    let item2: TextMenuItem = {
      content: 'create2',
      id: TextMenuItemId.of('create2'),
      icon: $r('app.media.startIcon'),
    };
    menuItems.push(item1);
    menuItems.unshift(item2);
    return menuItems;
  }
  onMenuItemClick = (menuItem: TextMenuItem, textRange: TextRange) => {
    if (menuItem.id.equals(TextMenuItemId.of('create2'))) {
      hilog.info(DOMAIN, 'testTag', '%{public}s', 'intercept id: create2 start:' + textRange.start + '; end:' + textRange.end);
      return true;
    }
    if (menuItem.id.equals(TextMenuItemId.of('prepare1'))) {
      hilog.info(DOMAIN, 'testTag', '%{public}s', 'intercept id: prepare1 start:' + textRange.start + '; end:' + textRange.end);
      return true;
    }
    if (menuItem.id.equals(TextMenuItemId.COPY)) {
      hilog.info(DOMAIN, 'testTag', '%{public}s', 'intercept COPY start:' + textRange.start + '; end:' + textRange.end);
      return true;
    }
    if (menuItem.id.equals(TextMenuItemId.SELECT_ALL)) {
      hilog.info(DOMAIN, 'testTag', '%{public}s', 'No interception SELECT_ALL start:' + textRange.start + '; end:' + textRange.end);
      return false;
    }
    return false;
  }
  onPrepareMenu = (menuItems: Array<TextMenuItem>) => {
    let item1: TextMenuItem = {
      content: 'prepare1_' + this.endIndex,
      icon: $r('app.media.startIcon'),
      id: TextMenuItemId.of('prepare1'),
    };
    menuItems.unshift(item1);
    return menuItems;
  }
  @State editMenuOptions: EditMenuOptions = {
    onCreateMenu: this.onCreateMenu,
    onMenuItemClick: this.onMenuItemClick,
    onPrepareMenu: this.onPrepareMenu
  };

  build() {
    NavDestination() {
    Column() {
      Text(this.text)
        .fontSize(20)
        .copyOption(CopyOptions.LocalDevice)
        .editMenuOptions(this.editMenuOptions)
        .margin({ top: 100 })
        .onTextSelectionChange((selectionStart: number, selectionEnd: number) => {
          this.endIndex = selectionEnd;
        })
    }
    .width('90%')
    .margin('5%')
    }
    // ...
  }
}

设置AI菜单

Text组件通过enableDataDetector和dataDetectorConfig属性实现AI菜单的显示。AI菜单的表现形式包括:单击AI实体(指可被识别的内容,包括地址、邮箱等)弹出菜单的实体识别选项,选中文本后,文本选择菜单与鼠标右键菜单中显示的实体识别选项。

从API version 20开始,支持在文本选择菜单与鼠标右键菜单中显示实体识别选项。当enableDataDetector设置为true,且copyOption设置为CopyOptions.LocalDevice时,该功能生效。菜单选项包括TextMenuItemId中的url(打开链接)、email(新建邮件)、phoneNumber(呼叫)、address(导航至该位置)、dateTime(新建日程提醒)。

该功能生效时,需选中范围内,包括一个完整的AI实体,才能展示对应的选项。

跨节点选中

在文章阅读、商品详情、协议条款等场景中,正文内容通常由标题、副标题、多个段落等独立Text组件分别承载。若无法跨越多个文本组件边界连续选择,多Text组件场景下选择体验存在割裂。

从API版本26.0.0开始,可通过SelectionContainer组件为多个Text子组件提供统一的跨节点文本选中、复制及菜单扩展能力。用户长按后可在不同Text节点之间拖拽连续选中文本,选中文本按Text组件视觉顺序拼接后统一复制,同时支持统一配置选中手柄颜色、高亮颜色、文本拼接策略及自定义菜单。SelectionContainer默认采用Stack布局,如需线性布局请在SelectionContainer内放置Column或Row等容器组件。

[h2]实现跨节点文本选中与复制

将多个Text组件置于SelectionContainer内,通过copyOption设置可复制范围,即可实现跨Text组件的连续选中和复制。通过caretColor和selectedBackgroundColor可统一设置选中手柄和高亮颜色,通过onTextSelectionChange、onWillCopy、onCopy可监听选中变化和复制流程。

// xxx.ets
import {
  SelectionContainer,
  SelectionContainerAttribute,
  SelectionContainerTextJoinStyle
} from '@kit.ArkUI';

@Entry
@Component
struct SelectionContainerDemo {
  @State selectedParts: string = '';
  @State copiedText: string = '';

  build() {
    NavDestination() {
      Column({ space: 16 }) {
        Text('请在下方区域跨节点选中文本')
          .fontSize(16)
          .fontColor('#999999')

        SelectionContainer() {
          Column({ space: 12 }) {
            Text('跨节点选中示例文章')
              .fontSize(24)
              .fontWeight(FontWeight.Bold)
            Text('在文章阅读场景中,标题与正文通常由多个Text组件分别承载。用户期望能够跨段落连续选中文本。')
              .fontSize(16)
            Text('SelectionContainer组件为多个文本节点提供跨节点选中、复制及菜单扩展能力,提升复杂文本布局中的交互体验。')
              .fontSize(16)
          }
        }
        .copyOption(CopyOptions.InApp)
        .textJoinStyle(SelectionContainerTextJoinStyle.NEWLINE)
        .caretColor(Color.Red)
        .selectedBackgroundColor('#33007DFF')
        .onTextSelectionChange((value: Array<string>) => {
          this.selectedParts = value.join(' | ');
        })
        .onWillCopy((value: string) => {
          this.copiedText = `准备复制:${value}`;
          return true;
        })
        .onCopy((value: string) => {
          this.copiedText = `复制成功:${value}`;
        })
        .border({ width: 1, color: '#DCDCDC' })
        .padding(12)
        .width('100%')

        Text(`选中内容:${this.selectedParts}`)
          .fontSize(14)
          .fontColor('#666666')
        Text(this.copiedText)
          .fontSize(14)
          .fontColor('#666666')
      }
      .width('100%')
      .padding(16)
    }
    // ...
  }
}

[h2]配置跨节点文本拼接方式

跨节点选中涉及多个Text组件时,复制到剪贴板的文本需按视觉顺序拼接。通过textJoinStyle可设置拼接方式:SelectionContainerTextJoinStyle.NEWLINE在不同文本节点间插入换行符,.DIRECT直接拼接不添加分隔符。

// xxx.ets
import {
  SelectionContainer,
  SelectionContainerAttribute,
  SelectionContainerTextJoinStyle
} from '@kit.ArkUI';

@Entry
@Component
struct SelectionContainerJoinStyle {
  @State joinStyle: SelectionContainerTextJoinStyle = SelectionContainerTextJoinStyle.NEWLINE;
  @State selectedText: string = '';
  @State copiedText: string = '';

  build() {
    NavDestination() {
      Column({ space: 16 }) {
        Row({ space: 12 }) {
          Button('换行拼接')
            .type(ButtonType.Capsule)
            .backgroundColor(this.joinStyle === SelectionContainerTextJoinStyle.NEWLINE ? '#007DFF' : '#E0E0E0')
            .fontColor(this.joinStyle === SelectionContainerTextJoinStyle.NEWLINE ? Color.White : Color.Black)
            .onClick(() => {
              this.joinStyle = SelectionContainerTextJoinStyle.NEWLINE;
            })
          Button('直接拼接')
            .type(ButtonType.Capsule)
            .backgroundColor(this.joinStyle === SelectionContainerTextJoinStyle.DIRECT ? '#007DFF' : '#E0E0E0')
            .fontColor(this.joinStyle === SelectionContainerTextJoinStyle.DIRECT ? Color.White : Color.Black)
            .onClick(() => {
              this.joinStyle = SelectionContainerTextJoinStyle.DIRECT;
            })
        }

        SelectionContainer() {
          Column({ space: 12 }) {
            Text('第一段文本:选中后复制的拼接方式由textJoinStyle决定。')
              .fontSize(18)
            Text('第二段文本:换行拼接时两段文本间会插入换行符,直接拼接时不插入任何分隔符。')
              .fontSize(18)
          }
        }
        .copyOption(CopyOptions.InApp)
        .textJoinStyle(this.joinStyle)
        .onTextSelectionChange((value: Array<string>) => {
          this.selectedText = value.join(' | ');
        })
        .onWillCopy((value: string) => {
          this.copiedText = value;
          return true;
        })
        .onCopy((value: string) => {
          this.copiedText = value;
        })
        .border({ width: 1, color: '#DCDCDC' })
        .padding(12)
        .width('100%')

        Text(`选中内容:${this.selectedText}`)
          .fontSize(14)
          .fontColor('#666666')
        Text('复制内容(体现拼接方式差异):')
          .fontSize(14)
          .fontColor('#666666')
        Text(this.copiedText)
          .fontSize(14)
          .fontColor('#333333')
          .backgroundColor('#F5F5F5')
          .padding(8)
          .borderRadius(4)
          .width('100%')
      }
      .width('100%')
      .padding(16)
    }
    // ...
  }
}

[h2]绑定自定义选择菜单

当系统默认菜单无法满足业务需求时,可通过bindSelectionMenu绑定完全自定义的选择菜单。与系统菜单不同,自定义菜单的内容、样式和触发条件均由开发者定义。

// xxx.ets
import {
  SelectionContainer,
  SelectionContainerAttribute,
  SelectionContainerMenuOptions,
  SelectionContainerTextJoinStyle
} from '@kit.ArkUI';

@Entry
@Component
struct SelectionContainerCustomMenu {
  @State selectedText: string = '';
  @State menuLog: string = '';

  build() {
    NavDestination() {
      Column({ space: 16 }) {
        Text('请在下方区域选中文本,体验自定义菜单')
          .fontSize(16)
          .fontColor('#999999')

        SelectionContainer() {
          Column({ space: 12 }) {
            Text('第一段文本:跨节点选中后长按可弹出完全自定义的选择菜单。')
              .fontSize(18)
            Text('第二段文本:通过bindSelectionMenu绑定自定义菜单,可替代系统默认菜单。')
              .fontSize(18)
          }
        }
        .copyOption(CopyOptions.InApp)
        .textJoinStyle(SelectionContainerTextJoinStyle.DIRECT)
        .bindSelectionMenu(
          TextSpanType.TEXT,
          this.menuBuilder,
          TextResponseType.DEFAULT,
          {
            onAppear: (text: string) => {
              this.menuLog = `菜单出现,选中文本:${text}`;
            },
            onDisappear: () => {
              this.menuLog = '菜单消失';
            }
          } as SelectionContainerMenuOptions
        )
        .onTextSelectionChange((value: Array<string>) => {
          this.selectedText = value.join(' | ');
        })
        .border({ width: 1, color: '#DCDCDC' })
        .padding(12)
        .width('100%')

        Text(`选中内容:${this.selectedText}`)
          .fontSize(14)
          .fontColor('#666666')
        Text(this.menuLog)
          .fontSize(14)
          .fontColor('#999999')
      }
      .width('100%')
      .padding(16)
    }
    // ...
  }

  @Builder
  menuBuilder() {
    Column() {
      Menu() {
        MenuItemGroup() {
          MenuItem({ content: '自定义复制', labelInfo: '' })
            .onClick(() => {
              this.menuLog = '点击了自定义复制';
            })
          MenuItem({ content: '自定义分享', labelInfo: '' })
            .onClick(() => {
              this.menuLog = '点击了自定义分享';
            })
          MenuItem({ content: '自定义翻译', labelInfo: '' })
            .onClick(() => {
              this.menuLog = '点击了自定义翻译';
            })
        }
      }
      .radius($r('sys.float.ohos_id_corner_radius_card'))
      .clip(true)
      .backgroundColor('#F0F0F0')
    }
  }
}

[h2]扩展系统菜单选项

若需在保留系统菜单项的基础上增加自定义选项,可通过editMenuOptions实现。该方式支持添加自定义菜单项、移除指定系统菜单项,并在onMenuItemClick回调中通过返回值拦截或放行系统默认行为。

// xxx.ets
import {
  OnMenuItemClickWithTextCallback,
  SelectionContainer,
  SelectionContainerAttribute,
  SelectionContainerEditMenuOptions,
  SelectionContainerTextJoinStyle
} from '@kit.ArkUI';

@Entry
@Component
struct SelectionContainerExtendMenu {
  @State selectedText: string = '';
  @State menuClickLog: string = '';

  onCreateMenu = (menuItems: Array<TextMenuItem>): Array<TextMenuItem> => {
    let targetIndex: number = menuItems.findIndex((item: TextMenuItem) => item.id.equals(TextMenuItemId.TRANSLATE));
    if (targetIndex !== -1) {
      menuItems.splice(targetIndex, 1);
    }
    targetIndex = menuItems.findIndex((item: TextMenuItem) => item.id.equals(TextMenuItemId.SEARCH));
    if (targetIndex !== -1) {
      menuItems.splice(targetIndex, 1);
    }
    let highlightItem: TextMenuItem = {
      content: '标注',
      id: TextMenuItemId.of('highlight')
    };
    let bookmarkItem: TextMenuItem = {
      content: '收藏',
      id: TextMenuItemId.of('bookmark')
    };
    let commentItem: TextMenuItem = {
      content: '批注',
      id: TextMenuItemId.of('comment')
    };
    menuItems.push(highlightItem);
    menuItems.push(bookmarkItem);
    menuItems.push(commentItem);
    return menuItems;
  }

  onMenuItemClick: OnMenuItemClickWithTextCallback = (menuItem: TextMenuItem, text: string): boolean => {
    this.menuClickLog = `点击菜单项:${menuItem.content},选中文本:${text}`;
    if (menuItem.id.equals(TextMenuItemId.COPY)) {
      this.selectedText = `已复制:${text}`;
      return true;
    }
    if (menuItem.id.equals(TextMenuItemId.SELECT_ALL)) {
      this.selectedText = `全选操作:${text}`;
      return false;
    }
    if (menuItem.id.equals(TextMenuItemId.of('highlight'))) {
      this.selectedText = `已标注:${text}`;
      return true;
    }
    if (menuItem.id.equals(TextMenuItemId.of('bookmark'))) {
      this.selectedText = `已收藏:${text}`;
      return true;
    }
    if (menuItem.id.equals(TextMenuItemId.of('comment'))) {
      this.selectedText = `已批注:${text}`;
      return true;
    }
    return false;
  }

  @State editMenuOptions: SelectionContainerEditMenuOptions = {
    onCreateMenu: this.onCreateMenu,
    onMenuItemClick: this.onMenuItemClick
  };

  build() {
    NavDestination() {
      Column({ space: 16 }) {
        Text('请在下方区域选中文本,体验扩展菜单')
          .fontSize(16)
          .fontColor('#999999')

        SelectionContainer() {
          Column({ space: 12 }) {
            Text('第一段文本:通过editMenuOptions可以在系统菜单基础上扩展自定义项。')
              .fontSize(18)
            Text('第二段文本:可以移除不需要的系统菜单项,并添加标注、收藏、批注等自定义项。')
              .fontSize(18)
          }
        }
        .copyOption(CopyOptions.InApp)
        .textJoinStyle(SelectionContainerTextJoinStyle.DIRECT)
        .editMenuOptions(this.editMenuOptions)
        .onTextSelectionChange((value: Array<string>) => {
          this.selectedText = value.join(' | ');
        })
        .border({ width: 1, color: '#DCDCDC' })
        .padding(12)
        .width('100%')

        Text(this.selectedText)
          .fontSize(14)
          .fontColor('#666666')
        Text(this.menuClickLog)
          .fontSize(14)
          .fontColor('#999999')
      }
      .width('100%')
      .padding(16)
    }
    // ...
  }
}

[h2]通过控制器关闭菜单与清除选中

在页面跳转、弹窗遮盖等场景下,需主动关闭选择菜单或清除选中状态。可通过SelectionContainerController的closeSelectionMenu关闭菜单,通过clearTextSelection清除选中态。

// xxx.ets
import {
  SelectionContainer,
  SelectionContainerAttribute,
  SelectionContainerController
} from '@kit.ArkUI';

@Entry
@Component
struct SelectionContainerControllerDemo {
  private controller: SelectionContainerController = new SelectionContainerController();
  @State selectedText: string = '';

  build() {
    NavDestination() {
      Column({ space: 16 }) {
        Text('请在下方区域跨节点选中文本,再点击按钮操作')
          .fontSize(16)
          .fontColor('#999999')

        SelectionContainer({ controller: this.controller }) {
          Column({ space: 12 }) {
            Text('第一段文本:选中后可通过控制器关闭选择菜单或清除选中文本。')
              .fontSize(18)
            Text('第二段文本:closeSelectionMenu仅关闭菜单,clearTextSelection会同时清除选中态。')
              .fontSize(18)
          }
        }
        .copyOption(CopyOptions.InApp)
        .onTextSelectionChange((value: Array<string>) => {
          this.selectedText = value.join(' | ');
        })
        .border({ width: 1, color: '#DCDCDC' })
        .padding(12)
        .width('100%')

        Row({ space: 12 }) {
          Button('关闭选择菜单')
            .type(ButtonType.Capsule)
            .onClick(() => {
              this.controller.closeSelectionMenu();
            })
          Button('清除文本选中')
            .type(ButtonType.Capsule)
            .onClick(() => {
              this.controller.clearTextSelection();
              this.selectedText = '';
            })
        }

        Text(`选中内容:${this.selectedText}`)
          .fontSize(14)
          .fontColor('#666666')
      }
      .width('100%')
      .padding(16)
    }
    // ...
  }
}

实现热搜榜

该示例通过maxLines、textOverflow、textAlign、constraintSize属性展示了热搜榜的效果。

@Entry
@Component
struct TextHotSearch {
  build() {
    NavDestination() {
      Column({ space: 12 }) {
        // ...
          Column() {
            Row() {
              Text('1').fontSize(14).fontColor(Color.Red).margin({ left: 10, right: 10 })
              // 请将$r('app.string.TextHotSearch_textContent_one')替换为实际资源文件,在本示例中该资源文件的value值为"我是热搜词条1"
              Text($r('app.string.TextHotSearch_textContent_one'))
                .fontSize(12)
                .fontColor(Color.Blue)
                .maxLines(1)
                .textOverflow({ overflow: TextOverflow.Ellipsis })
                .fontWeight(300)
              // 请将$r('app.string.TextHotSearch_textContent_two')替换为实际资源文件,在本示例中该资源文件的value值为"爆"
              Text($r('app.string.TextHotSearch_textContent_two'))
                .margin({ left: 6 })
                .textAlign(TextAlign.Center)
                .fontSize(10)
                .fontColor(Color.White)
                .fontWeight(600)
                .backgroundColor(0x770100)
                .borderRadius(5)
                .width(15)
                .height(14)
            }.width('100%').margin(5)

            Row() {
              Text('2').fontSize(14).fontColor(Color.Red).margin({ left: 10, right: 10 })
              /* 请将$r('app.string.TextHotSearch_textContent_three')替换为实际资源文件,
               * 在本示例中该资源文件的value值为"我是热搜词条2 我是热搜词条2 我是热搜词条2 我是热搜词条2 我是热搜词条2"
               */
              Text($r('app.string.TextHotSearch_textContent_three'))
                .fontSize(12)
                .fontColor(Color.Blue)
                .fontWeight(300)
                .constraintSize({ maxWidth: 200 })
                .maxLines(1)
                .textOverflow({ overflow: TextOverflow.Ellipsis })
              // 请将$r('app.string.TextHotSearch_textContent_four')替换为实际资源文件,在本示例中该资源文件的value值为"热"
              Text($r('app.string.TextHotSearch_textContent_four'))
                .margin({ left: 6 })
                .textAlign(TextAlign.Center)
                .fontSize(10)
                .fontColor(Color.White)
                .fontWeight(600)
                .backgroundColor(0xCC5500)
                .borderRadius(5)
                .width(15)
                .height(14)
            }.width('100%').margin(5)

            Row() {
              Text('3').fontSize(14).fontColor(Color.Orange).margin({ left: 10, right: 10 })
              // 请将$r('app.string.TextHotSearch_textContent_five')替换为实际资源文件,在本示例中该资源文件的value值为"我是热搜词条3"
              Text($r('app.string.TextHotSearch_textContent_five'))
                .fontSize(12)
                .fontColor(Color.Blue)
                .fontWeight(300)
                .maxLines(1)
                .constraintSize({ maxWidth: 200 })
                .textOverflow({ overflow: TextOverflow.Ellipsis })
              // 请将$r('app.string.TextHotSearch_textContent_four')替换为实际资源文件,在本示例中该资源文件的value值为"热"
              Text($r('app.string.TextHotSearch_textContent_four'))
                .margin({ left: 6 })
                .textAlign(TextAlign.Center)
                .fontSize(10)
                .fontColor(Color.White)
                .fontWeight(600)
                .backgroundColor(0xCC5500)
                .borderRadius(5)
                .width(15)
                .height(14)
            }.width('100%').margin(5)

            Row() {
              Text('4').fontSize(14).fontColor(Color.Grey).margin({ left: 10, right: 10 })
              /* 请将$r('app.string.TextHotSearch_textContent_six')替换为实际资源文件,
               * 在本示例中该资源文件的value值为"我是热搜词条4 我是热搜词条4 我是热搜词条4 我是热搜词条4 我是热搜词条4"
               */
              Text($r('app.string.TextHotSearch_textContent_six'))
                .fontSize(12)
                .fontColor(Color.Blue)
                .fontWeight(300)
                .constraintSize({ maxWidth: 200 })
                .maxLines(1)
                .textOverflow({ overflow: TextOverflow.Ellipsis })
            }.width('100%').margin(5)
          }.width('100%')
        // ...
      }
      .width('100%')
      .height('100%')
      .padding({ left: 12, right: 12 })
    }
    // ...
  }
}

示例代码