Designing Android Chat Bubble (Chat UI)
Creating Simple Android Chat Bubble or Chat UI Layout
Introduction
In this tip, we can see how a Chat Bubble (simply a Chat
UI) is designed.
Background
It's common nowadays to have a Chat feature in many of the Mobile Applications. So here, we discuss quickly about how to design one for us. We will try to keep it short and crisp as much as possible to maintain the simplicity.
Screenshot
Using the Code
We will need to use a ListView
to display the Chat list, listItem
for displaying the individual Chat messages , Adapter
to fill the ListView, Model Class
which represents the Chat Messages
and most important thing - 9 patch Images. The simplest explanation for 9 patch image will be an image which is set as background (Bubbles) and stretches accordingly with the text size. And its created just by saving any png image with an extension format. 9.png.
First, we start with creating an Activity
and its Layout
file which represents our Chat UI.
activity_chat.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="20dp">
<EditText
android:id="@+id/messageEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_toLeftOf="@+id/chatSendButton"
android:autoText="true"
android:hint="type message" />
<Button
android:id="@+id/chatSendButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:background="@color/background_floating_material_dark"
android:text="Send MSG"
android:textColor="@color/background_material_light"/>
<ListView
android:id="@+id/messagesContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="false"
android:layout_alignParentTop="false"
android:layout_marginBottom="20dp"
android:layout_above="@+id/messageEdit"
android:layout_below="@+id/meLbl"
android:layout_marginTop="10dp"
android:listSelector="@android:color/transparent"
android:transcriptMode="alwaysScroll"
android:divider="@null" />
<TextView
android:id="@+id/meLbl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left|center_vertical"
android:text="MySelf"
android:singleLine="false"
android:textSize="20dp" />
<TextView
android:id="@+id/friendLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="Friend"
android:textSize="20dp" />
</RelativeLayout>
</LinearLayout>
ChatActivity.java
public class ChatActivity extends ActionBarActivity {
private EditText messageET;
private ListView messagesContainer;
private Button sendBtn;
private ChatAdapter adapter;
private ArrayList<ChatMessage> chatHistory;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
initControls();
}
private void initControls() {
messagesContainer = (ListView) findViewById(R.id.messagesContainer);
messageET = (EditText) findViewById(R.id.messageEdit);
sendBtn = (Button) findViewById(R.id.chatSendButton);
TextView meLabel = (TextView) findViewById(R.id.meLbl);
TextView companionLabel = (TextView) findViewById(R.id.friendLabel);
RelativeLayout container = (RelativeLayout) findViewById(R.id.container);
companionLabel.setText("My Buddy");// Hard Coded
loadDummyHistory();
sendBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String messageText = messageET.getText().toString();
if (TextUtils.isEmpty(messageText)) {
return;
}
ChatMessage chatMessage = new ChatMessage();
chatMessage.setId(122);//dummy
chatMessage.setMessage(messageText);
chatMessage.setDate(DateFormat.getDateTimeInstance().format(new Date()));
chatMessage.setMe(true);
messageET.setText("");
displayMessage(chatMessage);
}
});
}
public void displayMessage(ChatMessage message) {
adapter.add(message);
adapter.notifyDataSetChanged();
scroll();
}
private void scroll() {
messagesContainer.setSelection(messagesContainer.getCount() - 1);
}
private void loadDummyHistory(){
chatHistory = new ArrayList<ChatMessage>();
ChatMessage msg = new ChatMessage();
msg.setId(1);
msg.setMe(false);
msg.setMessage("Hi");
msg.setDate(DateFormat.getDateTimeInstance().format(new Date()));
chatHistory.add(msg);
ChatMessage msg1 = new ChatMessage();
msg1.setId(2);
msg1.setMe(false);
msg1.setMessage("How r u doing???");
msg1.setDate(DateFormat.getDateTimeInstance().format(new Date()));
chatHistory.add(msg1);
adapter = new ChatAdapter(ChatActivity.this, new ArrayList<ChatMessage>());
messagesContainer.setAdapter(adapter);
for(int i=0; i<chatHistory.size(); i++) {
ChatMessage message = chatHistory.get(i);
displayMessage(message);
}
}
}
List Item which shows the individual chat messages and the Background of this text will be set as Chat Bubble (our 9 patch image).
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:orientation="vertical">
<TextView
android:id="@+id/txtInfo"
android:layout_width="wrap_content"
android:layout_height="30sp"
android:layout_gravity="right"
android:textSize="12sp"
android:textColor="@android:color/darker_gray" />
<LinearLayout
android:id="@+id/contentWithBackground"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:background="@drawable/in_message_bg"
android:paddingLeft="10dp"
android:paddingBottom="10dp"
android:orientation="vertical">
<TextView
android:id="@+id/txtMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/black"
android:maxWidth="240dp" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>
Now we require a Model Class for representing the Chat Message and an Adapter
to fill it for the ListView
.
ChatMessage.java
public class ChatMessage {
private long id;
private boolean isMe;
private String message;
private Long userId;
private String dateTime;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public boolean getIsme() {
return isMe;
}
public void setMe(boolean isMe) {
this.isMe = isMe;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public long getUserId() {
return userId;
}
public void setUserId(long userId) {
this.userId = userId;
}
public String getDate() {
return dateTime;
}
public void setDate(String dateTime) {
this.dateTime = dateTime;
}
}
ChatAdapter.java
As usual, we use ViewHolder
pattern for efficiency when recreating each items in the Listview
. LayoutParams
are for designing the Layout left or right aligned according to the Chat Message Send or Received. A dummy boolean value is used as the property to check whether its Send or Received for simplicity .
public class ChatAdapter extends BaseAdapter {
private final List<ChatMessage> chatMessages;
private Activity context;
public ChatAdapter(Activity context, List<ChatMessage> chatMessages) {
this.context = context;
this.chatMessages = chatMessages;
}
@Override
public int getCount() {
if (chatMessages != null) {
return chatMessages.size();
} else {
return 0;
}
}
@Override
public ChatMessage getItem(int position) {
if (chatMessages != null) {
return chatMessages.get(position);
} else {
return null;
}
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
ChatMessage chatMessage = getItem(position);
LayoutInflater vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null) {
convertView = vi.inflate(R.layout.list_item_chat_message, null);
holder = createViewHolder(convertView);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
boolean myMsg = chatMessage.getIsme() ;//Just a dummy check
//to simulate whether it me or other sender
setAlignment(holder, myMsg);
holder.txtMessage.setText(chatMessage.getMessage());
holder.txtInfo.setText(chatMessage.getDate());
return convertView;
}
public void add(ChatMessage message) {
chatMessages.add(message);
}
public void add(List<ChatMessage> messages) {
chatMessages.addAll(messages);
}
private void setAlignment(ViewHolder holder, boolean isMe) {
if (!isMe) {
holder.contentWithBG.setBackgroundResource(R.drawable.in_message_bg);
LinearLayout.LayoutParams layoutParams =
(LinearLayout.LayoutParams) holder.contentWithBG.getLayoutParams();
layoutParams.gravity = Gravity.RIGHT;
holder.contentWithBG.setLayoutParams(layoutParams);
RelativeLayout.LayoutParams lp =
(RelativeLayout.LayoutParams) holder.content.getLayoutParams();
lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT, 0);
lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
holder.content.setLayoutParams(lp);
layoutParams = (LinearLayout.LayoutParams) holder.txtMessage.getLayoutParams();
layoutParams.gravity = Gravity.RIGHT;
holder.txtMessage.setLayoutParams(layoutParams);
layoutParams = (LinearLayout.LayoutParams) holder.txtInfo.getLayoutParams();
layoutParams.gravity = Gravity.RIGHT;
holder.txtInfo.setLayoutParams(layoutParams);
} else {
holder.contentWithBG.setBackgroundResource(R.drawable.out_message_bg);
LinearLayout.LayoutParams layoutParams =
(LinearLayout.LayoutParams) holder.contentWithBG.getLayoutParams();
layoutParams.gravity = Gravity.LEFT;
holder.contentWithBG.setLayoutParams(layoutParams);
RelativeLayout.LayoutParams lp =
(RelativeLayout.LayoutParams) holder.content.getLayoutParams();
lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, 0);
lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
holder.content.setLayoutParams(lp);
layoutParams = (LinearLayout.LayoutParams) holder.txtMessage.getLayoutParams();
layoutParams.gravity = Gravity.LEFT;
holder.txtMessage.setLayoutParams(layoutParams);
layoutParams = (LinearLayout.LayoutParams) holder.txtInfo.getLayoutParams();
layoutParams.gravity = Gravity.LEFT;
holder.txtInfo.setLayoutParams(layoutParams);
}
}
private ViewHolder createViewHolder(View v) {
ViewHolder holder = new ViewHolder();
holder.txtMessage = (TextView) v.findViewById(R.id.txtMessage);
holder.content = (LinearLayout) v.findViewById(R.id.content);
holder.contentWithBG = (LinearLayout) v.findViewById(R.id.contentWithBackground);
holder.txtInfo = (TextView) v.findViewById(R.id.txtInfo);
return holder;
}
private static class ViewHolder {
public TextView txtMessage;
public TextView txtInfo;
public LinearLayout content;
public LinearLayout contentWithBG;
}
}
Now, that's all we need to accomplish our task of designing a Chat Bubble. This sample was created using Android Studio IDE. I have posted the complete code which can be downloaded.
Points of Interest
The sample is designed for making it easy to try it yourself. Happy coding. :) Please do contact me in case of any doubts or queries.
History
- 17th April, 2015: Initial version