一、自定义消息
如果基本消息类型不能满足你的需求,你可以根据实际业务需求自定义消息。下文以发送一条可跳转至浏览器的超文本作为自定义消息为例,帮助你快速了解实现流程。自定义消息样式如下:
二、展示自定义消息
在 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: 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.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? 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.MessageListMessageSummaryRegistryimport 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.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.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)