文档管理中心

Text组件实现长按全选内容并弹出自定义菜单

问题现象

如何对Text组件绑定长按手势,实现文本内容全选并弹出自定义菜单的功能?

背景知识

Text组件用于显示一段文本的组件,可以包含文字、图片等。该组件提供了bindSelectionMenu接口用于设置自定义选择菜单。bindSelectionMenu长按响应时长为600ms,长按达到时长后弹出自定义菜单。为了触发组件长按手势,可为组件绑定LongPressGesture事件,触发长按手势的最少手指数为1,最短长按时间若不设置,默认值为500毫秒。即达到500毫秒或者自定义时间时,会触发手势回调。

解决方案

  1. 通过bindSelectionMenu设置自定义菜单选项,同时注意如下关键点:
    • 设置spanType为TextSpanType.DEFAULT,响应多种文本类型。
    • 设置responseType为TextResponseType.DEFAULT,所有选中场景都触发自定义菜单。
    • 自定义菜单关闭时,取消选中区域。
  2. 通过LongPressGesture触发长按手势回调,将选中区域设置为所有文本。

代码实现如下:

收起
自动换行
深色代码主题
复制
  1. @Entry
  2. @Component
  3. struct TextExample {
  4. optionsPopup: string[] = ['搜索', '复制', '粘贴'];
  5. controller: TextController = new TextController();
  6. options: TextOptions = { controller: this.controller };
  7. @State start: number = -1; // 选中区域开始光标
  8. @State end: number = -1; // 选中区域结束光标
  9. build() {
  10. Column() {
  11. Column() {
  12. Text(undefined, this.options) {
  13. // 将Text内容设置为字图混合形式
  14. Span('Hello World')
  15. .fontSize(36)
  16. ImageSpan($r('app.media.startIcon'))
  17. .width('120px')
  18. .height('120px')
  19. .objectFit(ImageFit.Fill)
  20. .verticalAlign(ImageSpanAlignment.CENTER);
  21. }
  22. .selection(this.start, this.end) // 选中区域
  23. .copyOption(CopyOptions.InApp) // 设置可复制
  24. .bindSelectionMenu(TextSpanType.DEFAULT, this.LongPressTextCustomMenu, TextResponseType.DEFAULT) // 自定义菜单
  25. .gesture(
  26. LongPressGesture({ duration: 300 })
  27. .onAction(() => {
  28. // 调整选中区域,全选文本
  29. this.start = 0;
  30. this.end = 12;
  31. })
  32. )
  33. .width('100%')
  34. .height(150);
  35. }
  36. .width('100%')
  37. .backgroundColor(Color.White)
  38. .alignItems(HorizontalAlign.Start)
  39. .padding(25);
  40. }
  41. .height('100%');
  42. }
  43. // 自定义菜单
  44. @Builder
  45. LongPressTextCustomMenu() {
  46. Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.SpaceEvenly, alignItems: ItemAlign.Center }) {
  47. ForEach(this.optionsPopup, (item: string, index) => {
  48. Text(item).height('100%')
  49. .onClick(() => {
  50. // 取消选中
  51. this.start = -1;
  52. this.end = -1;
  53. // 关闭自定义菜单
  54. this.controller.closeSelectionMenu();
  55. });
  56. // 设置间隔
  57. if (index < this.optionsPopup.length - 1) {
  58. Divider().height(10).vertical(true);
  59. }
  60. });
  61. }
  62. .width(150)
  63. .height(40)
  64. .padding(10)
  65. .shadow({
  66. radius: 20,
  67. color: '#f3f5f7',
  68. offsetX: 0,
  69. offsetY: 10
  70. })
  71. .borderRadius(20)
  72. }
  73. }

运行效果如下:

常见FAQ

Q:为什么不使用onAppear做弹出菜单前的全选或者onDisappear做关闭菜单后的取消选中操作?

A:菜单的重新生成会触发onAppear和onDisappear,重新生成的场景包括选中区域文本类型的变化(由纯文字变为图文混合等)。因此当拖动光标触发上述场景,onAppear会导致选中区域强制为全选,onDisappear会导致选中区域强制取消,不符合交互逻辑。

Q:为什么将responseType为TextResponseType.LONG_PRESS,长按后弹出系统菜单,滑动光标后变为自定义菜单?

A:通过section设置的光标滑动,会被认为是TextResponseType.SELECT,因此会触发系统菜单。滑动过程中需要手指长按,因此被识别为长按,唤起自定义菜单。

Q:点击文本外的按钮触发弹窗事件,Text的文本选中菜单消失了但是不走onDisappear回调,是什么原因?

A:系统弹窗弹出,文本菜单消失,只是隐藏了,依旧在组件树上,并没有销毁,不会走onDisappear回调,会触发onMenuHide事件。

Q:RichText是否能使用自定义菜单?

A:RichText无法通过自定义菜单控制文本选择,推荐使用Web组件或者使用RichEditor组件,禁用编辑功能和键盘,再实现自定义菜单。

Q:如何给TextInput的复制粘贴功能添加自定义功能点?

A:可以使用editMenuOptions设置自定义菜单扩展项。参考示例文本扩展自定义菜单

Q:Text组件内自定义Span无法添加点击事件,如何实现点击文本弹出菜单的功能?

A:通过onTextSelectionChange()方法来实现,根据选中的文本弹出菜单,文本选择的位置发生变化时,触发该回调。详情可参考官网:示例8(文本绑定自定义菜单)

在 FAQ 中进行搜索
请输入您想要搜索的关键词