# Customize Chat Message Bubble

When TapTalk.io framework is integrated to your app with the UI implementation method, you can add customized chat message bubbles to be used in chat.

### Adding a Custom Chat Bubble

You can check the following example to add a custom bubble to your chat.

{% hint style="warning" %}
**Note**: To add a custom bubble, you need to implement **recyclerView** dependency to your **app-level build.gradle** file.
{% endhint %}

{% tabs %}
{% tab title="app module build.gradle" %}

```java
implementation 'com.android.support:recyclerview-v7:28.0.0'
```

{% endtab %}
{% endtabs %}

#### 1. Create an XML layout file for the custom bubble

First, create a new XML layout resource file for your custom bubble, we will name it `my_custom_bubble_layout.xml`. You are free to customize the custom bubble layout as you wish. In this example, we will be using a simple image and text bubble.<br>

{% tabs %}
{% tab title="my\_custom\_bubble\_layout.xml" %}

```markup
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginStart="16dp"
    android:layout_marginEnd="16dp"
    android:layout_marginBottom="8dp"
    android:background="@color/tapWhite">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:adjustViewBounds="true"
        android:src="@mipmap/ic_launcher"
        app:layout_constraintTop_toTopOf="parent" />
    
    <!--We are using TapTalk'io's default message body style for this TextView-->
    <TextView
        android:id="@+id/textView"
        style="@style/tapLeftBubbleMessageBodyStyle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="8dp"
        app:layout_constraintTop_toBottomOf="@id/imageView"
        tools:text="Message body" />

</android.support.constraint.ConstraintLayout>
```

{% endtab %}
{% endtabs %}

![my\_custom\_bubble\_layout.xml](https://4266585843-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LfVupFSqh_qZAY9OiCO%2F-MITU-amd75K7ni1ppWQ%2F-MITUeTY7darodJ5FFO8%2F5e41486-cb1.png?alt=media\&token=df8430d0-1804-4f4f-b7c2-21c8409057c6)

{% hint style="info" %}
**Note:** TapTalk.io's default message bubble layouts generally have a `8dp` bottom margin and no top margin.
{% endhint %}

#### 2. Create a custom listener

In order to listen to event callbacks that happens in your custom bubble, we will create a custom interface that extends **TapTalkBaseCustomInterface**. We will name this interface `MyCustomBubbleListener`.

{% tabs %}
{% tab title="MyCustomBubbleListener.java" %}

```java
import io.taptalk.TapTalk.Interface.TapTalkBaseCustomInterface;
import io.taptalk.TapTalk.Model.TAPMessageModel;

...

public interface MyCustomBubbleListener extends TapTalkBaseCustomInterface {

    // Use this to notify that the user tapped your custom bubble
    void onBubbleTapped(TAPMessageModel message);
}
```

{% endtab %}

{% tab title="MyCustomBubbleListener.kt" %}

```kotlin
import io.taptalk.TapTalk.Interface.TapTalkBaseCustomInterface
import io.taptalk.TapTalk.Model.TAPMessageModel

...

interface MyCustomBubbleListener : TapTalkBaseCustomInterface {

    // Use this to notify that the user tapped your custom bubble
    fun onBubbleTapped(message: TAPMessageModel)
}
```

{% endtab %}
{% endtabs %}

#### 3. Create a custom bubble ViewHolder

To display your custom bubble layout in a chat room, you will need to create a custom ViewHolder that extends **TAPBaseChatViewHolder**. We will name this class `MyCustomBubbleViewHolder`.

{% tabs %}
{% tab title="MyCustomBubbleListener.java" %}

```java
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import io.taptalk.TapTalk.Model.TAPMessageModel;
import io.taptalk.TapTalk.View.Adapter.TAPBaseChatViewHolder;

public class MyCustomBubbleViewHolder extends TAPBaseChatViewHolder {

    private ImageView imageView;
    private TextView textView;

    private MyCustomBubbleListener listener; // The listener that you just created above will be used here

    // Add MyCustomBubbleListener to the constructor parameter
    MyCustomBubbleViewHolder(ViewGroup parent, int itemLayoutId, MyCustomBubbleListener listener) {
        super(parent, itemLayoutId);

        // Bind views on constructor
        imageView = itemView.findViewById(R.id.imageView);
        textView = itemView.findViewById(R.id.textView);

        // Pass the listener from parameter
        this.listener = listener;
    }

    @Override
    protected void onBind(TAPMessageModel message, int index) {
        super.onBind(message, index);

        // Set message text
        textView.setText(message.getBody());

        // Set click listener
        imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // Trigger listener and pass the clicked message
                listener.onBubbleTapped(message);
            }
        });
    }
}
```

{% endtab %}

{% tab title="MyCustomBubbleListener.kt" %}

```kotlin
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import io.taptalk.TapTalk.Model.TAPMessageModel
import io.taptalk.TapTalk.View.Adapter.TAPBaseChatViewHolder

class MyCustomBubbleViewHolder internal constructor(
    parent: ViewGroup,
    itemLayoutId: Int,
    private val listener: MyCustomBubbleListener
) : TAPBaseChatViewHolder(parent, itemLayoutId) {

    private val imageView: ImageView = itemView.findViewById(R.id.imageView)
    private val textView: TextView = itemView.findViewById(R.id.textView)

    override fun onBind(message: TAPMessageModel?, index: Int) {
        super.onBind(message, index)
          
        // Set message text
        textView.text = message!!.body
          
        // Pass the listener from parameter
        imageView.setOnClickListener { 
            // Trigger listener and pass the clicked message
            listener.onBubbleTapped(message)
        }
    }
}
```

{% endtab %}
{% endtabs %}

#### 4. Create a custom bubble class

Using all the things you have created above, we will create a custom bubble class that extends **TAPBaseCustomBubble**. This class instance will then be added to the custom bubble list.

{% tabs %}
{% tab title="MyCustomBubbleClass.java" %}

```java
import android.view.ViewGroup;

import io.taptalk.TapTalk.Helper.TAPBaseCustomBubble;
import io.taptalk.TapTalk.Model.TAPUserModel;
import io.taptalk.TapTalk.View.Adapter.TAPMessageAdapter;

public class MyCustomBubbleClass extends TAPBaseCustomBubble<MyCustomBubbleViewHolder, MyCustomBubbleListener> {

    public MyCustomBubbleClass(int customBubbleLayoutRes, int messageType, MyCustomBubbleListener listener) {
        super(customBubbleLayoutRes, messageType, listener);
    }

    @Override
    public MyCustomBubbleViewHolder createCustomViewHolder(ViewGroup viewGroup, TAPMessageAdapter tapMessageAdapter, TAPUserModel tapUserModel, MyCustomBubbleListener listener) {
        return new MyCustomBubbleViewHolder(viewGroup, getCustomBubbleLayoutRes(), listener);
    }
}
```

{% endtab %}

{% tab title="MyCustomBubbleClass.kt" %}

```kotlin
import android.view.ViewGroup

import io.taptalk.TapTalk.Helper.TAPBaseCustomBubble
import io.taptalk.TapTalk.Model.TAPUserModel
import io.taptalk.TapTalk.View.Adapter.TAPMessageAdapter

class MyCustomBubbleClass(
    customBubbleLayoutRes: Int,
    messageType: Int,
    listener: MyCustomBubbleListener
) : TAPBaseCustomBubble<MyCustomBubbleViewHolder, MyCustomBubbleListener>(
    customBubbleLayoutRes,
    messageType,
    listener
) {

    override fun createCustomViewHolder(
        viewGroup: ViewGroup,
        tapMessageAdapter: TAPMessageAdapter,
        tapUserModel: TAPUserModel,
        listener: MyCustomBubbleListener
    ): MyCustomBubbleViewHolder {
        return MyCustomBubbleViewHolder(viewGroup, customBubbleLayoutRes, listener)
    }
}
```

{% endtab %}
{% endtabs %}

#### 5. Add your custom bubble class

To add your custom bubble to TapTalk.io, use the **TapUI** class and call the `addCustomBubble()` method. This is typically done in your Application class, after `init()` has already been called.

{% tabs %}
{% tab title="Java" %}

```java
import io.taptalk.TapTalk.Helper.TapTalk;
import io.taptalk.TapTalk.Manager.TapUI;
import io.taptalk.TapTalk.Model.TAPMessageModel;

...

TapTalk.init(...);

// Insert the XML layout resource, message type, and your custom listener as parameter
TapUI.getInstance().addCustomBubble(new MyCustomBubbleClass(
    R.layout.my_custom_bubble_layout,
    3001,
    new MyCustomBubbleListener() {
        @Override
        public void onBubbleTapped(TAPMessageModel message) {
            // My custom bubble was tapped by user
        }
    }
));
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
import io.taptalk.TapTalk.Helper.TapTalk
import io.taptalk.TapTalk.Manager.TapUI
import io.taptalk.TapTalk.Model.TAPMessageModel

...

TapTalk.init(...)

// Insert the XML layout resource, message type, and your custom listener as parameter
TapUI.getInstance().addCustomBubble(MyCustomBubbleClass(
    R.layout.my_custom_bubble_layout,
    3001,
    object : MyCustomBubbleListener() {
        fun onBubbleTapped(message: TAPMessageModel) {
            // My custom bubble was tapped by user
        }
    }
))
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
**Note:** Please note that the message type for custom bubbles should start with 3 as the prefix. For more information, check the [message type page](https://docs.taptalk.io/powertalk-chat-sdk-documentation/powertalk-ios/message-type).
{% endhint %}

After completing all the steps above, your custom bubble will be ready to use.

### Showing Your Custom Bubble in Chat

You can use your customized chat bubble in a chat room by constructing a custom **TAPMessageModel** with [`constructTapTalkMessageModel()`](https://docs.taptalk.io/powertalk-chat-sdk-documentation/powertalk-android/broken-reference) method, then sending the constructed message to the chat room with [`sendCustomMessage()`](https://docs.taptalk.io/powertalk-chat-sdk-documentation/powertalk-android/broken-reference). Both methods can be accessed from the **TapCoreMessageManager** class.

To construct a message, you are required to have a **TAPRoomModel** as a parameter. You can use [`getPersonalChatRoom()`](https://docs.taptalk.io/powertalk-chat-sdk-documentation/powertalk-android/broken-reference) or [`getGroupChatRoom()`](https://docs.taptalk.io/powertalk-chat-sdk-documentation/group-chat/tapcore#get-group-chat-room) method in **TapCoreChatRoomManager** class to obtain a chat room model.

Below is an example to send a custom message with the steps mentioned above.

{% tabs %}
{% tab title="Java" %}

```java
import io.taptalk.TapTalk.Manager.TapCoreMessageManager;
import io.taptalk.TapTalk.Manager.TapCoreChatRoomManager;
import io.taptalk.TapTalk.Listener.TapCoreGetRoomListener;
import io.taptalk.TapTalk.Listener.TapCoreSendMessageListener;
import io.taptalk.TapTalk.Model.TAPMessageModel;
import io.taptalk.TapTalk.Model.TAPRoomModel;

...

// First, we will retrieve a TAPRoomModel required to construct a message
TapCoreChatRoomManager.getInstance().getPersonalChatRoom(RECIPIENT_USER, new TapCoreGetRoomListener() {
    @Override
    public void onSuccess(TAPRoomModel room) {
        // Retrieved room data successfully, we will use this room to construct a TAPMessageModel
        TAPMessageModel customBubbleMessage = TapCoreMessageManager.getInstance().constructTapTalkMessageModel(
            "This is a custom message", // MESSAGE_BODY
            room,                       // ROOM
            3001,                       // MESSAGE_TYPE
            null                        // MESSAGE_DATA
        );
      
        // We will then send the constructed message to its corresponding chat room
        TapCoreMessageManager.getInstance().sendCustomMessage(customBubbleMessage, new TapCoreSendMessageListener() {
            @Override
            public void onSuccess(TAPMessageModel message) {
                // Custom message was sent successfully
            }

            @Override
            public void onError(String errorCode, String errorMessage) {
                
            }
        });
    }
});
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
import io.taptalk.TapTalk.Manager.TapCoreMessageManager
import io.taptalk.TapTalk.Manager.TapCoreChatRoomManager
import io.taptalk.TapTalk.Listener.TapCoreGetRoomListener
import io.taptalk.TapTalk.Listener.TapCoreSendMessageListener
import io.taptalk.TapTalk.Model.TAPMessageModel
import io.taptalk.TapTalk.Model.TAPRoomModel

...

// First, we will retrieve a TAPRoomModel required to construct a message
TapCoreChatRoomManager.getInstance().getPersonalChatRoom(RECIPIENT_USER, object : TapCoreGetRoomListener() {
    override fun onSuccess(room: TAPRoomModel?) {
        // Retrieved room data successfully, we will use this room to construct a TAPMessageModel
        val customBubbleMessage = TapCoreMessageManager.getInstance().constructTapTalkMessageModel(
            "This is a custom message", // MESSAGE_BODY
            room!!,                     // ROOM
            3001,                       // MESSAGE_TYPE
            null                        // MESSAGE_DATA
        )
      
        // We will then send the constructed message to its corresponding chat room
        TapCoreMessageManager.getInstance().sendCustomMessage(customBubbleMessage, object : TapCoreSendMessageListener() {
            override fun onSuccess(message: TAPMessageModel?) {
                // Custom message was sent successfully
            }

            override fun onError(errorCode: String?, errorMessage: String?) {
                
            }
        })
    }
})
```

{% endtab %}
{% endtabs %}
