鸿蒙Text文本设置选中菜单和AI菜单

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

设置选中菜单

弹出选中菜单

设置Text被选中时,会弹出包含复制、翻译、搜索的菜单。 Text组件需要设置copyOption属性才可以被选中。

typescript 复制代码
@Entry
@Component
struct Index {
  build() {
    Column() {
      Text("这是一段文本,用来展示选中菜单")
        .fontSize(30)
        .copyOption(CopyOptions.InApp)
    }.width('100%').height('100%')
  }
}

Text组件通过设置bindSelectionMenu属性绑定自定义选择菜单。

typescript 复制代码
@Entry
@Component
struct Index {
  controller:TextController = new TextController()
  build() {
    Column() {
      Text("这是一段文本,用来展示选中菜单", {controller:this.controller})
        .fontSize(30)
        .copyOption(CopyOptions.InApp)
        .bindSelectionMenu(TextSpanType.TEXT, this.RightClickTextCustomMenu, TextResponseType.RIGHT_CLICK, {
          onAppear: () => {
            console.info('自定义选择菜单弹出时触发该回调');
          },
          onDisappear: () => {
            console.info('自定义选择菜单关闭时触发该回调');
          }
        })
    }.width('100%').height('100%')
  }

  @Builder
  RightClickTextCustomMenu() {
    Column() {
      Menu() {
        MenuItemGroup() {
          MenuItem({ startIcon: $r('app.media.app_icon'), content: "CustomMenu One", labelInfo: "" })
            .onClick(() => {
              this.controller.closeSelectionMenu();
            })
          MenuItem({ startIcon: $r('app.media.app_icon'), content: "CustomMenu Two", labelInfo: "" })
          MenuItem({ startIcon: $r('app.media.app_icon'), content: "CustomMenu Three", labelInfo: "" })
        }
      }.backgroundColor('#F0F0F0')
    }
  }
}

Text组件通过设置editMenuOptions属性扩展自定义选择菜单,可以设置扩展项的文本内容、图标以及回调方法。

typescript 复制代码
@Entry
@Component
struct Index {
  onCreateMenu = (menuItems: Array<TextMenuItem>) => {
    let item1: TextMenuItem = {
      content: 'customMenu1',
      icon: $r('app.media.app_icon'),
      id: TextMenuItemId.of('customMenu1'),
    };
    let item2: TextMenuItem = {
      content: 'customMenu2',
      id: TextMenuItemId.of('customMenu2'),
      icon: $r('app.media.app_icon'),
    };
    menuItems.push(item1);
    menuItems.unshift(item2);
    return menuItems;
  }

  onMenuItemClick = (menuItem: TextMenuItem, textRange: TextRange) => {
    if (menuItem.id.equals(TextMenuItemId.of("customMenu2"))) {
      console.info("拦截 id: customMenu2 start:" + textRange.start + "; end:" + textRange.end);
      return true;
    }
    if (menuItem.id.equals(TextMenuItemId.COPY)) {
      console.info("拦截 COPY start:" + textRange.start + "; end:" + textRange.end);
      return true;
    }
    if (menuItem.id.equals(TextMenuItemId.SELECT_ALL)) {
      console.info("不拦截 SELECT_ALL start:" + textRange.start + "; end:" + textRange.end);
      return false;
    }
    return false;
  };

  build() {
    Column() {
      Text('这是一段文本,用来展示选中菜单')
        .fontSize(20)
        .copyOption(CopyOptions.LocalDevice)
        .editMenuOptions({
          onCreateMenu: this.onCreateMenu, onMenuItemClick: this.onMenuItemClick
        })
    }.width('100%').height('100%')
  }
}

关闭选中菜单

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

  • 在Text组件区域内点击空白处,会正常关闭选中态和菜单;
  • 在Text组件区域外点击空白处,前提是Text组件设置selection属性,具体示例如下:
typescript 复制代码
@Entry
@Component
struct Index {
  @State text: string =
    'This is set selection to Selection text content This is set selection to Selection text content.';
  @State start: number = 0;
  @State end: number = 20;

  build() {
    Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Start, justifyContent: FlexAlign.Start }) {
      Text(this.text)
        .fontSize(12)
        .border({ width: 1 })
        .lineHeight(20)
        .margin(30)
        .copyOption(CopyOptions.InApp)
        .selection(this.start, this.end)
        .onTextSelectionChange((selectionStart, selectionEnd) => {
          this.start = selectionStart;
          this.end = selectionEnd;
        })
    }
    .height(600)
    .width(335)
    .borderWidth(1)
    .onClick(() => {
      this.start = -1;
      this.end = -1;
    })
  }
}

屏蔽系统服务类菜单

从API version 20开始,支持通过disableSystemServiceMenuItems屏蔽文本选择菜单内所有系统服务菜单项。

typescript 复制代码
import { TextMenuController } from '@kit.ArkUI';
@Entry
@Component
struct Index {
  aboutToAppear() {
    TextMenuController.disableSystemServiceMenuItems(true);
  }

  aboutToDisappear() {
    TextMenuController.disableSystemServiceMenuItems(false);
  }

  build() {
    Row() {
      Column() {
        Text("这是一段文本,长按弹出文本选择菜单")
          .height(60)
          .fontStyle(FontStyle.Italic)
          .fontWeight(FontWeight.Bold)
          .textAlign(TextAlign.Center)
          .copyOption(CopyOptions.InApp)
          .editMenuOptions({
            onCreateMenu: (menuItems: Array<TextMenuItem>) => {
                return menuItems;
            },
            onMenuItemClick: (menuItem: TextMenuItem, textRange: TextRange) => {
                return false;
            }
          })
      }.width('100%')
    }
    .height('100%')
  }
}

从API version 20开始,支持通过disableMenuItems屏蔽文本选择菜单内指定的系统服务菜单项。

typescript 复制代码
import { TextMenuController } from '@kit.ArkUI'
@Entry
@Component
struct Index {
  aboutToAppear() {
    TextMenuController.disableMenuItems([TextMenuItemId.SEARCH])
  }

  aboutToDisappear() {
    TextMenuController.disableMenuItems([])
  }

  build() {
    Row() {
      Column() {
        Text("这是一段文本,长按弹出文本选择菜单")
          .height(60)
          .fontStyle(FontStyle.Italic)
          .fontWeight(FontWeight.Bold)
          .textAlign(TextAlign.Center)
          .copyOption(CopyOptions.InApp)
          .editMenuOptions({
            onCreateMenu: (menuItems: Array<TextMenuItem>) => {
                return menuItems;
            },
            onMenuItemClick: (menuItem: TextMenuItem, textRange: TextRange) => {
                return false
            }
          })
      }.width('100%')
    }
    .height('100%')
  }
}

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

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

typescript 复制代码
@Entry
@Component
struct Index {
  @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"))) {
      console.info("拦截 id: create2 start:" + textRange.start + "; end:" + textRange.end);
      return true;
    }
    if (menuItem.id.equals(TextMenuItemId.of("prepare1"))) {
      console.info("拦截 id: prepare1 start:" + textRange.start + "; end:" + textRange.end);
      return true;
    }
    if (menuItem.id.equals(TextMenuItemId.COPY)) {
      console.info("拦截 COPY start:" + textRange.start + "; end:" + textRange.end);
      return true;
    }
    if (menuItem.id.equals(TextMenuItemId.SELECT_ALL)) {
      console.info("不拦截 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() {
    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实体,才能展示对应的选项。

  • 如果需要单击AI实体弹出菜单的实体识别选项,可以配置enableDataDetector为true。
  • 如果在单击的交互方式之外,还需要文本选择菜单与鼠标右键菜单中显示的实体识别选项,可以配置enableDataDetector为true,且copyOption设置为CopyOptions.LocalDevice,具体示例如下所示:
typescript 复制代码
@Entry
@Component
struct Index {
  build() {
    Column() {
      Text(
        '电话号码:' + '(86) (755) ********' + '\n' +
          '链接:' + 'www.********.com' + '\n' +
          '邮箱:' + '***@example.com' + '\n' +
          '地址:' + 'XX省XX市XX区XXXX' + '\n' +
          '时间:' + 'XX年XX月XX日XXXX'
      )
        .fontSize(16)
        .copyOption(CopyOptions.LocalDevice)
        .enableDataDetector(true)// 使能实体识别
        .dataDetectorConfig({
          // 配置识别样式
          // types可支持PHONE_NUMBER电话号码、URL链接、EMAIL邮箱、ADDRESS地址、DATE_TIME时间
          // types设置为null或者[]时,识别所有类型的实体
          types: [], onDetectResultUpdate: (result: string) => {
          }
        })
    }.width('100%').height('100%')
  }
}
  • 如果需要调整识别出的样式,可以通过dataDetectorConfig实现,具体可以参考TextDataDetectorConfig配置项。
  • 如果需要调整菜单的位置,可以通过editMenuOptions实现,具体可以参考示例文本扩展自定义菜单。

相关推荐
特立独行的猫a9 小时前
鸿蒙PC平台三方库移植实战:以libid3tag库为例(附完整移植流程)
华为·harmonyos·移植·鸿蒙pc·libid3tag
爱笑的眼睛1114 小时前
深入探讨HarmonyOS分布式剪贴板:技术原理与开发实践
华为·harmonyos
特立独行的猫a15 小时前
鸿蒙PC平台三方库移植实战:以libogg库移植为例(附完整移植流程与工具链配置)
华为·harmonyos·三方库移植·鸿蒙pc·libogg
爱笑的眼睛1115 小时前
深入理解HarmonyOS通知渠道与优先级设置:从基础到高级实践
华为·harmonyos
夏文强19 小时前
HarmonyOS开发者认证练习题-判断题
华为·harmonyos
Kisang.19 小时前
【HarmonyOS】ArkTS的多线程并发(下)——线程间通信对象的传递
华为·typescript·harmonyos·鸿蒙
柒儿吖1 天前
Qt for HarmonyOS 3D图片轮播组件开源鸿蒙开发实战
qt·3d·harmonyos
爱笑的眼睛111 天前
HarmonyOS分布式输入法开发:实现多设备无缝输入体验
华为·harmonyos
爱笑的眼睛111 天前
深入HarmonyOS打印服务:从基础到高级应用开发
华为·harmonyos