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
build.gradle
:
dependencies {
compile 'com.daasuu:BubbleLayout:1.1.1'
}
Some important attributes
<com.daasuu.bl.BubbleLayout
, it has some notable attributes:bl_arrowWidth
: Width of the arrow, default value is 8dpbl_arrowHeight
:Height of the arrow, default value is 8dpbl_arrowPosition
: Position of the arrow, default value is 12dpbl_cornersRadius
: Corner radius of theBubbleLayout
, default value is 0dpbl_bubbleColor
: Color of theBubbleLayout
, default color is whitebl_arrowDirection
: Drawing position of the arrow :left
ortop
orright
orbottom
, default position isleft
.
activity_example.xml
Running this activity, we'll have this output:
<?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>
Buidling a instant message application UI
ListView
like this:
activitiy_main.xml
Create a POJO for the project:
<?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>
ChatMessage.java
And the 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;
}
}
ListView
adapter, based on ArrayAdapter
:
ChatListAdapter.java
Code for our main activity, getting messages from an 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);
}
}
}
EditText
:
MainActivity.java
As you can see, I use 2 layouts for message items, based on it's in or out message, this is these layout file:
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();
}
}
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
Running this application, you'll see this output:
<?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>