帮你快速理解、总结文档立即下载

Android

最近更新时间:2026-07-14 10:01:46

我的收藏
TUIKit 默认实现了文本、图片、语音、视频、文件等基本消息类型的发送和展示,如果这些消息类型满足不了您的需求,您可以新增自定义消息类型。

自定义消息

如果基本消息类型不能满足您的需求,您可以根据实际业务需求自定义消息。下文以发送一条可跳转至浏览器的超文本作为自定义消息为例,帮助您快速了解实现流程。自定义消息样式如下图所示:


展示自定义消息

在 UIKit 中,您只需要向消息列表配置 ChatMessageListConfig 注册一个自定义消息渲染器 MessageContentRenderer,再把该配置传入聊天页面 ChatPageView 即可。
自定义消息的内容以 String 格式存储在 CustomMessagePayload.customData 中,建议使用 JSON 格式,并通过 businessID 字段来唯一标识这条消息类型。

定义自定义消息数据结构

定义一个数据类,用于描述自定义消息解析后的结构,并提供一个从 JSON String 解析的方法。以上述包含链接及文本的自定义消息格式举例:
import com.google.gson.Gson

data 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 并把文字和链接渲染到视图上,同时为气泡添加点击跳转事件。bindViewMessageRenderContext 提供了当前消息 message、主题颜色 colors、会话 conversationID 等上下文。
示例代码如下:
import android.content.Context
import android.content.Intent
import android.graphics.Paint
import android.net.Uri
import android.util.TypedValue
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import io.trtc.tuikit.chat.uikit.components.messagelist.ui.MessageContentRenderer
import io.trtc.tuikit.chat.uikit.components.messagelist.ui.MessageRenderConfig
import io.trtc.tuikit.chat.uikit.components.messagelist.ui.MessageRenderContext
import io.trtc.tuikit.atomicxcore.api.message.CustomMessagePayload

class CustomLinkMessageRenderer : MessageContentRenderer {

override val renderConfig: MessageRenderConfig
get() = 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 CustomLinkMessageView
val colors = context.colors
val isSelf = context.message.isSentBySelf

val payload = context.message.messagePayload as? CustomMessagePayload
val linkMessage = CustomLinkMessage.from(payload?.customData)
val text = linkMessage?.text.orEmpty()
val link = linkMessage?.link.orEmpty().trim()
val hasLink = link.isNotEmpty()
val canOpenLink = hasLink && !context.isMultiSelectMode

messageView.textView.text = text
messageView.linkView.text = "查看详情"
messageView.linkView.visibility = if (hasLink) View.VISIBLE else View.GONE
messageView.textView.setTextColor(
if (isSelf) colors.textColorAntiPrimary else colors.textColorPrimary
)
messageView.linkView.setTextColor(
if (isSelf) colors.textColorAntiPrimary else colors.textColorLink
)

messageView.isClickable = canOpenLink
messageView.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: TextView
val linkView: TextView

init {
orientation = VERTICAL
val density = resources.displayMetrics.density
val 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 = true
maxLines = 1
ellipsize = null
layoutParams = LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT
)
}
linkView = TextView(context).apply {
setTextSize(TypedValue.COMPLEX_UNIT_SP, 14f)
setLineSpacing(0f, 1.2f)
isSingleLine = true
maxLines = 1
ellipsize = null
layoutParams = 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.ChatMessageListConfig
import io.trtc.tuikit.chat.uikit.pages.ChatPageView
import 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? CustomMessagePayload
CustomLinkMessage.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.MessageListMessageSummaryRegistry
import io.trtc.tuikit.atomicxcore.api.message.CustomMessagePayload

// App 初始化时调用一次即可
MessageListMessageSummaryRegistry.setCustomMessageSummary(
businessID = "text_link",
summaryProvider = { summaryContext ->
val payload = summaryContext.message.messagePayload as? CustomMessagePayload
CustomLinkMessage.from(payload?.customData)?.text
}
)
实现后的会话列表预览效果如下:


发送自定义消息

注意:
自定义消息内容必须为 JSON 格式。其中 “businessID” 字段为必填项。可根据业务需求添加其他字段,单条消息大小上限为 12KB。例如:
{
"businessID":"text_link",
"link":"https://cloud.tencent.com/document/product/269/3794",
"text":"欢迎加入云通信IM大家庭!"
}
发送自定义消息的基本步骤:把要发送的内容转成 JSON String 放入 SendMessagePayload.CustomSendMessagePayloadcustomData 中,再调用 MessageInputStoresendMessage 接口发送。核心代码如下:
import com.google.gson.Gson
import io.trtc.tuikit.atomicxcore.api.CompletionHandler
import io.trtc.tuikit.atomicxcore.api.message.MessageInputStore
import io.trtc.tuikit.atomicxcore.api.message.SendMessagePayload

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) {}
}
)
如下图所示,可以在输入框的“更多”面板中增加一个自定义消息发送按钮,点击时创建并发送上述自定义消息:

通过 ChatMessageInputConfig.setCustomMenuActionProvider 追加一个自定义功能项,并把配置一并传入 ChatPageView.setup 即可。示例代码如下:
import com.google.gson.Gson
import io.trtc.tuikit.chat.uikit.components.messageinput.config.ChatMessageInputConfig
import io.trtc.tuikit.chat.uikit.components.messageinput.data.MessageInputMenuAction
import io.trtc.tuikit.chat.uikit.pages.ChatPageView
import io.trtc.tuikit.atomicxcore.api.CompletionHandler
import io.trtc.tuikit.atomicxcore.api.message.MessageInputStore
import io.trtc.tuikit.atomicxcore.api.message.SendMessagePayload

val messageInputConfig = ChatMessageInputConfig().setCustomMenuActionProvider { actionContext ->
val conversationID = actionContext.conversationID
listOf(
MessageInputMenuAction(
title = "自定义消息",
// 替换成你自己的图标资源,例如 R.drawable.ic_custom_message
iconResID = 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
)