TUIKit 默认实现了文本、图片、语音、视频、文件等基本消息类型的发送和展示,如果这些消息类型满足不了您的需求,您可以新增自定义消息类型。
自定义消息
如果基本消息类型不能满足您的需求,您可以根据实际业务需求自定义消息。下文以发送一条可跳转至浏览器的超文本作为自定义消息为例,帮助您快速了解实现流程。自定义消息样式如下图所示:

展示自定义消息
在 UIKit 中,您只需要向消息列表配置
ChatMessageListConfig 注册一个自定义消息渲染器 MessageContentRenderer,再把该配置传入聊天页面 ChatPageView 即可。自定义消息的内容以 String 格式存储在
CustomMessagePayload.customData 中,建议使用 JSON 格式,并通过 businessID 字段来唯一标识这条消息类型。定义自定义消息数据结构
定义一个数据类,用于描述自定义消息解析后的结构,并提供一个从 JSON String 解析的方法。以上述包含链接及文本的自定义消息格式举例:
import com.google.gson.Gsondata class CustomLinkMessage(val businessID: String? = null,val text: String? = null,val link: String? = null,) {companion object {fun from(customData: String?): CustomLinkMessage? {if (customData.isNullOrBlank()) {return null}return runCatching {Gson().fromJson(customData, CustomLinkMessage::class.java)}.getOrNull()}}}
实现自定义消息渲染器
实现一个
MessageContentRenderer,在 createView 中创建气泡内容视图,在 bindView 中解析 CustomLinkMessage 并把文字和链接渲染到视图上,同时为气泡添加点击跳转事件。bindView 的 MessageRenderContext 提供了当前消息 message、主题颜色 colors、会话 conversationID 等上下文。示例代码如下:
import android.content.Contextimport android.content.Intentimport android.graphics.Paintimport android.net.Uriimport android.util.TypedValueimport android.view.Viewimport android.view.ViewGroupimport android.widget.LinearLayoutimport android.widget.TextViewimport io.trtc.tuikit.chat.uikit.components.messagelist.ui.MessageContentRendererimport io.trtc.tuikit.chat.uikit.components.messagelist.ui.MessageRenderConfigimport io.trtc.tuikit.chat.uikit.components.messagelist.ui.MessageRenderContextimport io.trtc.tuikit.atomicxcore.api.message.CustomMessagePayloadclass CustomLinkMessageRenderer : MessageContentRenderer {override val renderConfig: MessageRenderConfigget() = MessageRenderConfig(showMessageMeta = true, useDefaultBubble = true)override fun createView(context: Context, parent: ViewGroup): View {return CustomLinkMessageView(context).apply {layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT)}}override fun bindView(view: View, context: MessageRenderContext) {val messageView = view as CustomLinkMessageViewval colors = context.colorsval isSelf = context.message.isSentBySelfval payload = context.message.messagePayload as? CustomMessagePayloadval linkMessage = CustomLinkMessage.from(payload?.customData)val text = linkMessage?.text.orEmpty()val link = linkMessage?.link.orEmpty().trim()val hasLink = link.isNotEmpty()val canOpenLink = hasLink && !context.isMultiSelectModemessageView.textView.text = textmessageView.linkView.text = "查看详情"messageView.linkView.visibility = if (hasLink) View.VISIBLE else View.GONEmessageView.textView.setTextColor(if (isSelf) colors.textColorAntiPrimary else colors.textColorPrimary)messageView.linkView.setTextColor(if (isSelf) colors.textColorAntiPrimary else colors.textColorLink)messageView.isClickable = canOpenLinkmessageView.setOnClickListener(if (canOpenLink) View.OnClickListener {openLink(view.context, link)} else null)}private fun openLink(context: Context, link: String) {runCatching {val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link))intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)context.startActivity(intent)}}}private class CustomLinkMessageView(context: Context) : LinearLayout(context) {val textView: TextViewval linkView: TextViewinit {orientation = VERTICALval density = resources.displayMetrics.densityval horizontalPadding = (16 * density).toInt()val verticalPadding = (12 * density).toInt()setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding)textView = TextView(context).apply {setTextSize(TypedValue.COMPLEX_UNIT_SP, 16f)setLineSpacing(0f, 1.3f)isSingleLine = truemaxLines = 1ellipsize = nulllayoutParams = LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT)}linkView = TextView(context).apply {setTextSize(TypedValue.COMPLEX_UNIT_SP, 14f)setLineSpacing(0f, 1.2f)isSingleLine = truemaxLines = 1ellipsize = nulllayoutParams = LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT).apply {topMargin = (8 * density).toInt()}}addView(textView)addView(linkView)}}
注册渲染器并自定义会话摘要
在创建聊天页面
ChatPageView 时,通过 ChatMessageListConfig.setCustomMessageRenderer 注册自定义消息渲染器,并把配置传入 ChatPageView.setup。其中 businessID 必须与消息 customData 中的 businessID 字段一致,TUIKit 会据此找到对应的渲染器。示例代码如下:
import io.trtc.tuikit.chat.uikit.components.messagelist.config.ChatMessageListConfigimport io.trtc.tuikit.chat.uikit.pages.ChatPageViewimport io.trtc.tuikit.atomicxcore.api.message.CustomMessagePayload// 自定义消息 businessID(注意不能重复,区分大小写)val businessID = "text_link"val messageListConfig = ChatMessageListConfig().setCustomMessageRenderer(businessID = businessID,renderer = CustomLinkMessageRenderer(),summaryProvider = { summaryContext ->val payload = summaryContext.message.messagePayload as? CustomMessagePayloadCustomLinkMessage.from(payload?.customData)?.text})val chatPageView = ChatPageView(context)chatPageView.setup(conversationID = conversationID,messageListConfig = messageListConfig)
setCustomMessageRenderer 中的 summaryProvider 用于生成消息列表内部的文字摘要(例如引用回复的预览)。如果还需要自定义会话列表中“最后一条消息”的预览文案,请在 App 初始化时通过全局注册表 MessageListMessageSummaryRegistry.setCustomMessageSummary 注册。示例代码如下:
import io.trtc.tuikit.chat.uikit.components.messagelist.utils.MessageListMessageSummaryRegistryimport io.trtc.tuikit.atomicxcore.api.message.CustomMessagePayload// App 初始化时调用一次即可MessageListMessageSummaryRegistry.setCustomMessageSummary(businessID = "text_link",summaryProvider = { summaryContext ->val payload = summaryContext.message.messagePayload as? CustomMessagePayloadCustomLinkMessage.from(payload?.customData)?.text})
实现后的会话列表预览效果如下:

发送自定义消息
注意:
自定义消息内容必须为 JSON 格式。其中 “businessID” 字段为必填项。可根据业务需求添加其他字段,单条消息大小上限为 12KB。例如:
{"businessID":"text_link","link":"https://cloud.tencent.com/document/product/269/3794","text":"欢迎加入云通信IM大家庭!"}
发送自定义消息的基本步骤:把要发送的内容转成 JSON String 放入
SendMessagePayload.CustomSendMessagePayload 的 customData 中,再调用 MessageInputStore 的 sendMessage 接口发送。核心代码如下:import com.google.gson.Gsonimport io.trtc.tuikit.atomicxcore.api.CompletionHandlerimport io.trtc.tuikit.atomicxcore.api.message.MessageInputStoreimport io.trtc.tuikit.atomicxcore.api.message.SendMessagePayloadval customLinkMessage = CustomLinkMessage(businessID = "text_link",text = "欢迎加入云通信IM大家庭!",link = "https://cloud.tencent.com/document/product/269/3794")val payload = SendMessagePayload.CustomSendMessagePayload(customData = Gson().toJson(customLinkMessage),description = customLinkMessage.text)MessageInputStore.create(conversationID).sendMessage(payload = payload,completion = object : CompletionHandler {override fun onSuccess() {}override fun onFailure(code: Int, desc: String) {}})
如下图所示,可以在输入框的“更多”面板中增加一个自定义消息发送按钮,点击时创建并发送上述自定义消息:

通过
ChatMessageInputConfig.setCustomMenuActionProvider 追加一个自定义功能项,并把配置一并传入 ChatPageView.setup 即可。示例代码如下:import com.google.gson.Gsonimport io.trtc.tuikit.chat.uikit.components.messageinput.config.ChatMessageInputConfigimport io.trtc.tuikit.chat.uikit.components.messageinput.data.MessageInputMenuActionimport io.trtc.tuikit.chat.uikit.pages.ChatPageViewimport io.trtc.tuikit.atomicxcore.api.CompletionHandlerimport io.trtc.tuikit.atomicxcore.api.message.MessageInputStoreimport io.trtc.tuikit.atomicxcore.api.message.SendMessagePayloadval messageInputConfig = ChatMessageInputConfig().setCustomMenuActionProvider { actionContext ->val conversationID = actionContext.conversationIDlistOf(MessageInputMenuAction(title = "自定义消息",// 替换成你自己的图标资源,例如 R.drawable.ic_custom_messageiconResID = android.R.drawable.ic_menu_send,order = 10,onClick = {val customLinkMessage = CustomLinkMessage(businessID = "text_link",text = "欢迎加入云通信IM大家庭!",link = "https://cloud.tencent.com/document/product/269/3794")val payload = SendMessagePayload.CustomSendMessagePayload(customData = Gson().toJson(customLinkMessage),description = customLinkMessage.text)MessageInputStore.create(conversationID).sendMessage(payload = payload,completion = object : CompletionHandler {override fun onSuccess() {}override fun onFailure(code: Int, desc: String) {}})}))}chatPageView.setup(conversationID = conversationID,messageListConfig = messageListConfig,messageInputConfig = messageInputConfig)