SelectableMenu - 鸿蒙聊天文本选择菜单组件

文本选择菜单组件,主要用于聊天对话框中的长按文本选择和操作功能。

功能特性

  • 文本选择:支持长按选择文本
  • 自动全选:长按时默认选中全部文本内容
  • 自定义菜单:支持自定义菜单项,包括图标、标题和操作

安装使用

bash 复制代码
ohpm install @cxy/selecteablemenu

或在项目 oh-package.json5 添加依赖,然后同步项目

json 复制代码
{
  "dependencies": {
    "@cxy/selecteablemenu": "^1.0.0"
  }
}

完整示例 - 查看demo

typescript 复制代码
import {
  MenuContainer, SelectableMenuItem, SelectableModel, SelectableText
} from '@cxy/selecteablemenu'

enum MessageType {
  Text = 0,
  Image = 1
}

/**
 * ChatMessage 需要继承至 SelectableModel
 */
@Observed
class ChatMessage extends SelectableModel {
  id: number = 0
  type: MessageType = MessageType.Text
  text: string = ''
  imageUrl: ResourceStr = ''

  constructor(id: number) {
    super()
    this.id = id
  }

  /**
   * 覆盖父类方法,消息是否可以复制
   * @returns
   */
  public canCopy(): boolean {
    return this.type === MessageType.Text && this.text.length > 0
  }

  /**
   * 覆盖父类方法,消息可复制的文本
   * @returns
   */
  public copyText(): string {
    return this.text
  }

  /**
   * 覆盖父类方法,消息的弹出菜单项
   * @returns 菜单数组
   */
  public getMenus(): SelectableMenuItem[] {
    const menus: SelectableMenuItem[] = []

    if (this.canCopy()) {
      menus.push({
        title: '复制',
        icon: $r("app.media.copy"),
        action: () => {
          // 复制文本到剪贴板
          const text = this.copyText()
          // TODO: 调用系统复制API
          // ...

          this.onDidMenuItem?.(true)
        }
      })
    }

    if (this.canCopy() && this.selectionStart >= 0 && this.selectionEnd > 0 &&
      this.selectionEnd - this.selectionStart < this.copyText().length) {
      menus.push({
        title: '全选',
        icon: $r("app.media.edit"),
        action: () => {
          this.onDidMenuItem?.(false, true)
        }
      })
    }

    menus.push({
      title: '转发',
      icon: $r("app.media.forward"),
      action: () => {
        // TODO: 处理转发逻辑
        // ...

        this.onDidMenuItem?.()
      }
    })

    menus.push({
      title: '收藏',
      icon: $r("app.media.favor"),
      action: () => {
        // TODO: 处理收藏逻辑
        // ...

        this.onDidMenuItem?.()
      }
    })

    menus.push({
      title: '删除',
      icon: $r("app.media.delete"),
      action: () => {
        // TODO: 处理删除逻辑
        // ...

        this.onDidMenuItem?.()
      }
    })

    menus.push({
      title: '多选',
      icon: $r("app.media.sort"),
      action: () => {
        // TODO: 处理多选逻辑
        // ...

        this.onDidMenuItem?.()
      }
    })

    return menus
  }
}

@Entry
@Component
struct Index {
  @State messages: Array<ChatMessage> = []

  aboutToAppear(): void {
    this.initMessages()
  }

  initMessages() {
    const message1 = new ChatMessage(1)
    message1.text = '这是一条可以长按选择的文本消息'

    const message2 = new ChatMessage(2)
    message2.type = MessageType.Image
    message2.imageUrl = $r('app.media.foreground')

    const message3 = new ChatMessage(3)
    message3.text = 'Hello, SelectableMenu:https://github.com/iHongRen/SelectableMenu'

    const message4 = new ChatMessage(4)
    message4.text = `          登太白楼醉书
危楼接汉倚云端,飞檐欲触斗牛寒。
我来携酒凌层巅,长风万里送征鞍。
苍冥浩渺浮元气,大江奔涌走狂澜。
吴楚烟霞开画障,齐鲁青峦列翠屏。
手抚栏杆邀明月,明月笑我醉颜酡。
谪仙何处寻踪迹?唯有诗魂绕此阁。
忆昔长安金銮殿,曾奉新词动御筵。
沉香亭北花如锦,兴庆池边柳似绵。
力士脱靴羞权贵,贵妃研墨媚君前。
一朝谤起离京阙,扁舟载酒下江天。
采石矶头捞夜月,绿萝溪畔枕云眠。
兴来落笔摇五岳,诗成笑傲凌九天。
我今踏迹追先哲,胸中有气吞河山。
豪饮千觞心未醉,狂歌一曲意难阑。
浮云蔽日何须叹,世事如棋莫久看。
且放白鹿青崖间,漫随鸥鸟狎清川。
醉里不知天地阔,醒来犹见斗牛悬。
墨痕洒处风雷动,笔底龙蛇走蜿蜒。
人生得意须尽欢,莫使金樽空对月。
他年若遂凌云志,再驾长风访列仙。
银河为砚天为纸,写尽人间万古篇。
此楼此景长相忆,醉卧烟霞不知还。`

    this.messages.push(message1)
    this.messages.push(message2)
    this.messages.push(message3)
    this.messages.push(message4)
  }

  build() {
    Navigation() {
      List({ space: 12 }) {
        ForEach(this.messages, (message: ChatMessage) => {
          ListItem() {
            if (message.type === MessageType.Text) {
              // 文本消息
              Column() {
                SelectableText({
                  model: message,
                  // text: message.text,
                  fontSize: 16,
                  fontColor: '#333333',
                  caretColor: '#007AFF',
                  selectedBackgroundColor: '#33007AFF',
                  enableDataDetector: true
                }) {
                  Span(message.text) //SelectableText子组件与Text的子组件一致
                }
              }
              .backgroundColor('#ffffff')
              .borderRadius(12)
              .padding(16)
              .alignItems(HorizontalAlign.Start)

            } else if (message.type === MessageType.Image) {
              // 图片消息
              MenuContainer({
                model: message,

              }) {
                Image(message.imageUrl)
                  .width(150)
              }
            }
          }

        }, (message: ChatMessage) => message.id.toString())
      }
      .backgroundColor('#f5f5f5')
      .padding(15)
      .layoutWeight(1)
    }
    .title('聊天消息')
    .titleMode(NavigationTitleMode.Mini)
    .mode(NavigationMode.Stack)
    .parallelGesture(
      TapGesture()
        .onAction((event) => {
          SelectableModel.onPageTap?.(event)
        })
    )
  }
}

API 参考

SelectableText

可选择文本组件,继承Text组件大部分属性并扩展文本选择功能,增加属性如下:

属性 类型 默认值 说明
model SelectableModel - 数据模型实例
popupColor ResourceColor '#e6000000' 弹出菜单背景色
popupRadius number 5 弹出菜单圆角
placement Placement Placement.Top 弹出菜单位置
menuItemWidth number 50 (vp) 菜单项的宽度
maxColumnCount number 5 最大的显示列数

菜单容器组件,适用于非文本选择的组件,菜单配置属性同上。

SelectableModel

数据模型基类,提供选择状态管理和事件回调。

属性 类型 默认值 说明
onPageTap (event?: BaseGestureEvent) => void - 页面点击时调用,隐藏菜单
selectionStart number -1 选择的起始位置
selectionEnd number -1 弹出菜单圆角
longpressPopup boolean false 非文本组件长按弹窗是否显示
onDidMenuItem (isCopy?: boolean, isSelectAll?: boolean) => void - 菜单项点击时,需调用这个方法。isCopy 是否是复制项点击,isSelectAll 是否是全选点击

需要继承实现的方法:

方法 返回值 说明
canCopy() boolean 是否可复制
copyText() string 返回可复制的文本
getMenus() SelectableMenuItem[] 返回菜单项数组

作者

@仙银 鸿蒙相关开源作品

1、hpack - 鸿蒙内部测试分发,一键脚本打包工具

2、Open-in-DevEco-Studio - macOS 直接在 Finder 工具栏上,使用 DevEco-Studio 打开鸿蒙工程。

3、cxy-theme - DevEco-Studio 绿色背景主题

4、harmony-udid-tool - 简单易用的 HarmonyOS 设备 UDID 获取工具,适用于非开发人员。

5、SandboxFinder - 鸿蒙沙箱文件浏览器

6、WebServer - 鸿蒙轻量级Web服务器框架

7、SelectableMenu - 适用于聊天对话框中的文本选择菜单

🌟 如果项目对你有帮助,欢迎持续关注和 Star ,赞助

相关推荐
缘澄6 分钟前
ArkUI基础篇-组件事件
harmonyos·arkui
鸿蒙先行者8 分钟前
HarmonyOS与OpenHarmony区别分析
harmonyos
li理22 分钟前
鸿蒙NEXT渲染控制全面解析:从条件渲染到混合开发
harmonyos
li理1 小时前
鸿蒙Next组件扩展全面解析:从构建函数到样式复用的完整指南
前端·harmonyos
博客园团队1 小时前
2025 HarmonyOS 创新赛正式启动,百万大奖等你挑战!
harmonyos
AlbertZein3 小时前
HarmonyOS5 一顿饭时间 —— LRU、磁盘缓存与内存优化的结合
架构·harmonyos
Andy_GF3 小时前
鸿蒙Next在蒲公英平台分发测试包
android·ios·harmonyos
zkmall6 小时前
ZKmall开源商城多端兼容实践:鸿蒙、iOS、安卓全平台适配的技术路径
ios·开源·harmonyos
奶糖不太甜1 天前
鸿蒙5.1.0及ArkTS新特性
harmonyos·arkts