Skip to main content
A MessageTemplate defines and customizes both the structure and the behavior of the MessageBubble. It acts as a schema for creating message bubble components, allowing you to manage the appearance and interactions of message bubbles within your application.

When to Use This

  • Customize the header, content, footer, bottom, or status info view of a message bubble
  • Replace the default bubble layout with a completely custom view
  • Add or modify the long-press options on a message bubble
  • Create a new template for a custom message type (e.g., a contact card)
  • Change how a specific message type (text, image, etc.) renders in the MessageList

Prerequisites

  • CometChat Android UI Kit dependency added
  • CometChatUIKit.init() called and completed
  • A logged-in CometChat user
  • Familiarity with the MessageList component

Quick Start

  1. Get the list of existing message templates from the data source:
val messageTemplates = CometChatUIKit.getDataSource()
    .getMessageTemplates(messageList.additionParameter)
  1. Find the template for the message type you want to customize:
for (template in messageTemplates) {
    if (template.type == UIKitConstants.MessageType.TEXT) {
        // Customize text message template
    }
}
  1. Customize a view on the matched template:
template.setContentView(object : MessagesViewHolderListener() {
    override fun createView(
        context: Context,
        cometChatMessageBubble: CometChatMessageBubble,
        messageBubbleAlignment: UIKitConstants.MessageBubbleAlignment
    ): View {
        return LayoutInflater.from(context).inflate(R.layout.your_custom_layout, null)
    }

    override fun bindView(
        context: Context, view: View, baseMessage: BaseMessage,
        messageBubbleAlignment: UIKitConstants.MessageBubbleAlignment,
        viewHolder: RecyclerView.ViewHolder, list: List<BaseMessage>, i: Int
    ) {
        // Bind your custom view data here
    }
})
  1. Apply the modified templates to the MessageList:
messageList.setTemplates(messageTemplates)

Core Concepts

MessageBubble Structure

The MessageBubble structure is broken down into these views:
  1. Leading view — Sender’s avatar. Left for incoming, right for outgoing.
  2. Header view — Sender’s name. Useful in group chats.
  3. Content view — Core message content (text, images, videos, etc.).
  4. Bottom view — Additional elements like link previews or “load more” buttons.
  5. Footer view — Timestamp and delivery/read status.

Template Properties

  • Type: setType() maps the template to a CometChatMessage type.
  • Category: setCategory() links the template with a message category.
messageTemplate.setType(UIKitConstants.MessageType.CUSTOM)
messageTemplate.setCategory(UIKitConstants.MessageCategory.CUSTOM)

Implementation

Header View

Customize the header area above the content view.
template.setHeaderView(object : MessagesViewHolderListener() {
    override fun createView(
        context: Context,
        cometChatMessageBubble: CometChatMessageBubble,
        messageBubbleAlignment: UIKitConstants.MessageBubbleAlignment
    ): View {
        return layoutInflater.inflate(R.layout.message_template_header_view, null)
    }

    override fun bindView(
        context: Context, view: View, baseMessage: BaseMessage,
        messageBubbleAlignment: UIKitConstants.MessageBubbleAlignment,
        viewHolder: RecyclerView.ViewHolder, list: List<BaseMessage>, i: Int
    ) {
        val textView = view.findViewById<TextView>(R.id.name_with_status)
        textView.text = baseMessage.sender.name + " • 🗓️ In meeting"
    }
})

Content View

Customize the main content area of the message bubble.
template.setContentView(object : MessagesViewHolderListener() {
    override fun createView(
        context: Context,
        cometChatMessageBubble: CometChatMessageBubble,
        messageBubbleAlignment: UIKitConstants.MessageBubbleAlignment
    ): View {
        return LayoutInflater.from(context).inflate(R.layout.image_bubble_content_view, null)
    }

    override fun bindView(
        context: Context, view: View, baseMessage: BaseMessage,
        messageBubbleAlignment: UIKitConstants.MessageBubbleAlignment,
        viewHolder: RecyclerView.ViewHolder, list: List<BaseMessage>, i: Int
    ) {
        val imageBubble = view.findViewById<CometChatImageBubble>(R.id.imageBubble)
        val mediaMessage = baseMessage as MediaMessage
        val attachment = mediaMessage.attachment
        imageBubble.setImageUrl(
            Utils.getFileFromLocalPath(mediaMessage),
            attachment?.fileUrl ?: "",
            attachment?.fileExtension?.equals("gif", ignoreCase = true) ?: false
        )
    }
})

Status Info View

Customize the status info area (delivery/read status indicators).
// Replace status info with a minimal spacer
template.setStatusInfoView(object : MessagesViewHolderListener() {
    override fun createView(
        context: Context,
        cometChatMessageBubble: CometChatMessageBubble,
        messageBubbleAlignment: UIKitConstants.MessageBubbleAlignment
    ): View {
        val view = View(context)
        view.layoutParams = LinearLayout.LayoutParams(1.dp, 12.dp)
        return view
    }

    override fun bindView(
        context: Context, view: View, baseMessage: BaseMessage,
        messageBubbleAlignment: UIKitConstants.MessageBubbleAlignment,
        viewHolder: RecyclerView.ViewHolder, list: List<BaseMessage>, i: Int
    ) { }
})

// Move status content to footer
template.setFooterView(object : MessagesViewHolderListener() {
    override fun createView(
        context: Context,
        cometChatMessageBubble: CometChatMessageBubble,
        messageBubbleAlignment: UIKitConstants.MessageBubbleAlignment
    ): View {
        return LayoutInflater.from(context).inflate(R.layout.status_info_layout, null)
    }

    override fun bindView(
        context: Context, view: View, baseMessage: BaseMessage,
        messageBubbleAlignment: UIKitConstants.MessageBubbleAlignment,
        viewHolder: RecyclerView.ViewHolder, list: List<BaseMessage>, i: Int
    ) {
        val tvTime = view.findViewById<TextView>(R.id.time)
        val receipt = view.findViewById<CometChatMessageReceipt>(R.id.receipt)
        if (messageBubbleAlignment == UIKitConstants.MessageBubbleAlignment.RIGHT) {
            receipt.visibility = View.VISIBLE
            receipt.setMessageReceipt(MessageReceiptUtils.MessageReceipt(baseMessage))
        } else {
            receipt.visibility = View.GONE
        }
        tvTime.text = SimpleDateFormat("hh:mm a").format(baseMessage.sentAt * 1000)
    }
})

Bottom View

Extend the bubble with additional elements beneath the content view.
template.setBottomView(object : MessagesViewHolderListener() {
    override fun createView(
        context: Context,
        cometChatMessageBubble: CometChatMessageBubble,
        messageBubbleAlignment: UIKitConstants.MessageBubbleAlignment
    ): View {
        return LayoutInflater.from(context).inflate(R.layout.message_template_bottom_view, null)
    }

    override fun bindView(
        context: Context, view: View, baseMessage: BaseMessage,
        messageBubbleAlignment: UIKitConstants.MessageBubbleAlignment,
        viewHolder: RecyclerView.ViewHolder, list: List<BaseMessage>, i: Int
    ) {
        // Bind bottom view data
    }
})
Customize the footer area (timestamp and delivery status).
template.setFooterView(object : MessagesViewHolderListener() {
    override fun createView(
        context: Context,
        cometChatMessageBubble: CometChatMessageBubble,
        messageBubbleAlignment: UIKitConstants.MessageBubbleAlignment
    ): View {
        return LayoutInflater.from(context).inflate(R.layout.message_template_footer_view, null)
    }

    override fun bindView(
        context: Context, view: View, baseMessage: BaseMessage,
        messageBubbleAlignment: UIKitConstants.MessageBubbleAlignment,
        viewHolder: RecyclerView.ViewHolder, list: List<BaseMessage>, i: Int
    ) {
        val tvTime = view.findViewById<TextView>(R.id.time)
        tvTime.text = SimpleDateFormat("hh:mm a").format(baseMessage.sentAt * 1000)
    }
})

Bubble View

Replace the entire message bubble with a fully custom layout.
template.setBubbleView(object : MessagesViewHolderListener() {
    override fun createView(
        context: Context,
        cometChatMessageBubble: CometChatMessageBubble,
        messageBubbleAlignment: UIKitConstants.MessageBubbleAlignment
    ): View {
        return LayoutInflater.from(context).inflate(R.layout.custom_bubble_view, null)
    }

    override fun bindView(
        context: Context, view: View, baseMessage: BaseMessage,
        messageBubbleAlignment: UIKitConstants.MessageBubbleAlignment,
        viewHolder: RecyclerView.ViewHolder, list: List<BaseMessage>, i: Int
    ) {
        // Bind entire custom bubble
    }
})

Options

Customize the long-press action menu for a message type.
template.setOptions { context, baseMessage, isLeftAligned ->
    listOf(
        CometChatPopupMenu.MenuItem(
            id = "bookmark",
            name = "Bookmark",
            onClick = { /* handle */ }
        )
    )
}

Creating a New Template

Create a template for a custom message type (e.g., a contact card):
val contactTemplate = CometChatMessageTemplate()
contactTemplate.setType("contact")
contactTemplate.setCategory(UIKitConstants.MessageCategory.CUSTOM)

contactTemplate.setContentView(object : MessagesViewHolderListener() {
    override fun createView(
        context: Context,
        cometChatMessageBubble: CometChatMessageBubble,
        messageBubbleAlignment: UIKitConstants.MessageBubbleAlignment
    ): View {
        return LayoutInflater.from(context).inflate(R.layout.contact_card_bubble, null)
    }

    override fun bindView(
        context: Context, view: View, baseMessage: BaseMessage,
        messageBubbleAlignment: UIKitConstants.MessageBubbleAlignment,
        viewHolder: RecyclerView.ViewHolder, list: List<BaseMessage>, i: Int
    ) {
        val customMessage = baseMessage as CustomMessage
        val name = view.findViewById<TextView>(R.id.contact_name)
        name.text = customMessage.customData?.getString("name") ?: ""
    }
})

// Add to existing templates
val templates = CometChatUIKit.getDataSource()
    .getMessageTemplates(messageList.additionParameter)
templates.add(contactTemplate)
messageList.setTemplates(templates)

Next Steps

Message List

Display messages in a conversation

Message Bubble Styling

Style individual bubble types

Component Styling

Detailed styling reference

View Slots

Replace specific UI regions