Bubble Layout (Chat message) by third-party library in Android

    As you have read at my previous post, by 9-patch images, you can design the bubble layout to create UI for a chat application and there is still the best way to designing for making floating messages. But more over, as a...lazy a developer like me and if you don't have an available design, you can use a third-party to create the bubble layouts for your app!
    In this post, I will present a library called BubbleLayout  to make a simple Bubble View for Android app with custom stroke width and color, arrow size, position and direction.

Setting up the library

    To use in an Android Studio project, add it as a compile dependency in the app module’s build.gradle:
dependencies {
    compile 'com.daasuu:BubbleLayout:1.1.1'
}

Some important attributes

    The widget to make a bubble view in this library is <com.daasuu.bl.BubbleLayout, it has some notable attributes:
  • bl_arrowWidth: Width of the arrow, default value is 8dp
  • bl_arrowHeight:Height of the arrow, default value is 8dp
  • bl_arrowPosition: Position of the arrow, default value is 12dp
  • bl_cornersRadius: Corner radius of the BubbleLayout, default value is 0dp
  • bl_bubbleColor: Color of the BubbleLayout, default color is white
  • bl_arrowDirection: Drawing position of the arrow : left or top or right or bottom, default position is left.
    For example, we have this layout (xml) file:
activity_example.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.daasuu.bubblelayout.MainActivity">

    <com.daasuu.bl.BubbleLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="8dp"
        app:bl_arrowDirection="left"
        app:bl_arrowHeight="8dp"
        app:bl_arrowPosition="16dp"
        app:bl_arrowWidth="8dp"
        app:bl_strokeWidth="1dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginRight="4dp"
            android:text="BubbleLayout"
            android:textColor="@android:color/holo_red_dark" />

    </com.daasuu.bl.BubbleLayout>

    <com.daasuu.bl.BubbleLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="12dp"
        android:padding="8dp"
        app:bl_arrowDirection="top"
        app:bl_arrowHeight="8dp"
        app:bl_arrowPosition="12dp"
        app:bl_arrowWidth="8dp"
        app:bl_bubbleColor="@android:color/holo_blue_light"
        app:bl_cornersRadius="8dp">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <ImageView
                android:layout_width="20dp"
                android:layout_height="20dp"
                android:src="@mipmap/ic_launcher" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:layout_marginLeft="4dp"
                android:text="BubbleLayout"
                android:textColor="@android:color/holo_red_dark" />

        </LinearLayout>

    </com.daasuu.bl.BubbleLayout>


    <com.daasuu.bl.BubbleLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="12dp"
        android:padding="8dp"
        app:bl_arrowDirection="right"
        app:bl_arrowHeight="8dp"
        app:bl_arrowPosition="16dp"
        app:bl_arrowWidth="8dp"
        app:bl_cornersRadius="6dp"
        app:bl_strokeWidth="1dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginRight="4dp"
            android:text="BubbleLayout"
            android:textColor="@android:color/holo_red_dark" />

    </com.daasuu.bl.BubbleLayout>

    <com.daasuu.bl.BubbleLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="12dp"
        android:padding="8dp"
        app:bl_arrowDirection="bottom"
        app:bl_arrowHeight="8dp"
        app:bl_arrowPosition="16dp"
        app:bl_arrowWidth="8dp"
        app:bl_bubbleColor="@android:color/holo_green_light"
        app:bl_cornersRadius="6dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginRight="4dp"
            android:text="BubbleLayout"
            android:textColor="@android:color/white" />

    </com.daasuu.bl.BubbleLayout>


    <Button
        android:id="@+id/btn_popup"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="32dp"
        android:text="Bubble Popup" />

</LinearLayout>
    Running this activity, we'll have this output:

Buidling a instant message application UI

    The bubble layout usually used for making chat layouts (bubble messages). Now, we'll build a chat application layout with using this library. To do it, firstly, creating an activity layout with ListView like this:
activitiy_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="info.devexchanges.bubblechatui.MainActivity">

    <ListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:paddingBottom="20dp"
        android:divider="@null"
        android:layout_height="wrap_content" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="0.1"
        android:layout_alignParentBottom="true"
        android:orientation="horizontal">

        <EditText
            android:id="@+id/msg_type"
            android:layout_width="0dp"
            android:paddingLeft="@dimen/activity_horizontal_margin"
            android:layout_height="match_parent"
            android:layout_weight="0.7"
            android:hint="Input message" />

        <Button
            android:id="@+id/btn_chat_send"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="0.3"
            android:background="@color/background_floating_material_dark"
            android:text="Send"
            android:textColor="@color/background_material_light" />
    </LinearLayout>
</RelativeLayout>
Create a POJO for the project:
ChatMessage.java
package info.devexchanges.bubblechatui;

public class ChatMessage {

    private boolean isMine;
    private String message;

    public ChatMessage(String message, boolean isMine) {
        this.isMine = isMine;
        this.message = message;
    }

    public boolean isMine() {
        return isMine;
    }

    public void setMine(boolean mine) {
        isMine = mine;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
    And the ListView adapter, based on ArrayAdapter:
ChatListAdapter.java
package info.devexchanges.bubblechatui;

import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

import java.util.ArrayList;

public class ChatListAdapter extends ArrayAdapter<ChatMessage>{

    private Activity activity;
    private ArrayList<ChatMessage> chatMessages;

    public ChatListAdapter(Activity context, int resource, ArrayList<ChatMessage> objects) {
        super(context, resource, objects);
        this.activity =context;
        this.chatMessages = objects;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);

        int layoutResource; // determined by view type
        ChatMessage chatMessage = getItem(position);

        if (chatMessage.isMine()) {
            layoutResource = R.layout.item_out;
        } else {
            layoutResource = R.layout.item_in;
        }

        if (convertView != null) {
            holder = (ViewHolder) convertView.getTag();
        } else {
            convertView = inflater.inflate(layoutResource, parent, false);
            holder = new ViewHolder(convertView);
            convertView.setTag(holder);
        }

        //set message content
        holder.message.setText(chatMessage.getMessage());

        return convertView;
    }

    @Override
    public int getViewTypeCount() {
        // return the total number of view types. this value should never change
        // at runtime
        return 2;
    }

    @Override
    public int getItemViewType(int position) {
        // return a value between 0 and (getViewTypeCount - 1)
        return position % 2;
    }

    static class ViewHolder {
        private TextView message;

        public ViewHolder(View v) {
            message = (TextView)v.findViewById(R.id.text);
        }
    }
}
    Code for our main activity, getting messages from an EditText:
MainActivity.java
package info.devexchanges.bubblechatui;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.Random;

public class MainActivity extends AppCompatActivity {

    private ListView listView;
    private View btnSend;
    private EditText input;
    private ArrayList<ChatMessage> messages;
    private ChatListAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        messages = new ArrayList<>();

        listView = (ListView) findViewById(R.id.list_view);
        btnSend = findViewById(R.id.btn_chat_send);
        input = (EditText) findViewById(R.id.msg_type);

        //set ListView adapter first
        adapter = new ChatListAdapter(this, R.layout.item_out, messages);
        listView.setAdapter(adapter);

        //event for button SEND
        btnSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (input.getText().toString().trim().equals("")) {
                    Toast.makeText(MainActivity.this, "Please input some text...", Toast.LENGTH_SHORT).show();
                } else {
                    //add message to list
                    ChatMessage chatMessage = new ChatMessage(input.getText().toString(), isMine());
                    messages.add(chatMessage);
                    adapter.notifyDataSetChanged();
                    listView.setSelection(adapter.getCount() - 1); //always scroll to bottom of list view
                    input.setText("");
                }
            }
        });
    }

    private boolean isMine() {
        Random random = new Random();
        Log.d("Main", "" + random.nextBoolean());
        return random.nextBoolean();
    }
}
    As you can see, I use 2 layouts for message items, based on it's in or out message, this is these layout file:
item_in.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <com.daasuu.bl.BubbleLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="12dp"
        android:padding="8dp"
        app:bl_arrowDirection="left"
        app:bl_arrowHeight="10dp"
        app:bl_arrowPosition="50dp"
        android:layout_alignParentRight="true"
        app:bl_arrowWidth="15dp"
        app:bl_bubbleColor="@android:color/holo_blue_light"
        app:bl_cornersRadius="15dp">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <ImageView
            android:layout_width="wrap_content"
            android:contentDescription="@null"
            android:layout_height="wrap_content"
            android:src="@mipmap/out" />

        <TextView
            android:id="@+id/text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginLeft="5dp"
            android:textColor="@android:color/holo_red_dark" />

    </LinearLayout>
    </com.daasuu.bl.BubbleLayout>

</RelativeLayout>
item_out.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <com.daasuu.bl.BubbleLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="12dp"
        android:padding="8dp"
        app:bl_arrowDirection="right"
        app:bl_arrowHeight="8dp"
        app:bl_arrowPosition="50dp"
        app:bl_arrowWidth="15dp"
        app:bl_bubbleColor="@android:color/holo_green_light"
        app:bl_cornersRadius="6dp">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <ImageView
                android:contentDescription="@null"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@mipmap/in" />

            <TextView
                android:id="@+id/text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:layout_marginLeft="4dp"
                android:textColor="@android:color/white" />

        </LinearLayout>
    </com.daasuu.bl.BubbleLayout>
</RelativeLayout>
Running this application, you'll see this output:

Final thoughts

    I've just presented the usage of BubbleLayout library, hope this is helpful with your quick designing a simple chat application interface. Beside that, using 9-patch images like description at my previous post still is a better way, you should take a glance! Thanks for reading! Finally, the full project code now available on @Github.

Share


Previous post
« Prev Post
Next post
Next Post »