Implementing a Recent Conversation List in Android
To manage recent chats in an Android instant messaging application, an Activity is required to listen for incoming message broadcasts, update the dataset, and refresh the ListView. The implementation involves a main Activity, a custom adapter for UI binding, and a model class for conversation data.
The ConversationListActivity registers a local broadcast receiver. When a new message arrives, the receiver extracts the account ID, message body, and timestamp. It removes any existing conversation entries for the same user to prevent duplicate rows in the list, appends the new chat snippet, and updates the adapter. Clicking a list item routes the user to the actual chat interface.
public class ConversationListActivity extends Activity {
private ListView conversationListView;
private List<ChatSnippet> conversationList = new ArrayList<>();
private String[] incomingPayload;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_conversations);
IntentFilter filter = new IntentFilter("com.app.messenger.NEW_MESSAGE");
registerReceiver(new MessageUpdateReceiver(), filter);
conversationListView = findViewById(R.id.conversation_list);
conversationListView.setOnItemClickListener((parent, view, position, id) -> {
Intent chatIntent = new Intent(ConversationListActivity.this, ChatDetailActivity.class);
chatIntent.putExtra("userId", Integer.parseInt(incomingPayload[0]));
chatIntent.putExtra("username", "");
startActivity(chatIntent);
});
}
private class MessageUpdateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
incomingPayload = intent.getStringArrayExtra("payload");
int targetAccountId = Integer.parseInt(incomingPayload[0]);
String messageBody = incomingPayload[1];
String timestamp = incomingPayload[2];
Toast.makeText(context, "New message from " + targetAccountId + ": " + messageBody, Toast.LENGTH_SHORT).show();
// Remove existing conversation entries for this user to avoid duplicates
conversationList.removeIf(snippet -> snippet.getAccountId() == targetAccountId);
conversationList.add(new ChatSnippet(5, targetAccountId, String.valueOf(targetAccountId), messageBody, timestamp, false));
conversationListView.setAdapter(new ChatSnippetAdapter(ConversationListActivity.this, conversationList));
unregisterReceiver(this);
}
}
}
The ChatSnippetAdapter extends BaseAdapter to map the ChatSnippet model to the list item layout. It uses the ViewHolder pattern to optimize performence by avoiding repeated findViewById calls during scrolling.
public class ChatSnippetAdapter extends BaseAdapter {
private final Context mContext;
private final List<ChatSnippet> mSnippets;
private final LayoutInflater mInflater;
public ChatSnippetAdapter(Context context, List<ChatSnippet> snippets) {
this.mContext = context;
this.mSnippets = snippets;
this.mInflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return mSnippets.size();
}
@Override
public Object getItem(int position) {
return mSnippets.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_conversation, parent, false);
holder = new ViewHolder();
holder.avatarImg = convertView.findViewById(R.id.img_user_avatar);
holder.nicknameTxt = convertView.findViewById(R.id.txt_username);
holder.messageTxt = convertView.findViewById(R.id.txt_last_message);
holder.readStatusImg = convertView.findViewById(R.id.img_read_indicator);
holder.timeTxt = convertView.findViewById(R.id.txt_message_time);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
ChatSnippet snippet = mSnippets.get(position);
holder.nicknameTxt.setText(snippet.getNick());
holder.messageTxt.setText(snippet.getContent());
holder.readStatusImg.setImageResource(R.drawable.ic_unread_indicator);
holder.timeTxt.setText(snippet.getTime());
return convertView;
}
static class ViewHolder {
ImageView avatarImg;
TextView nicknameTxt;
TextView messageTxt;
ImageView readStatusImg;
TextView timeTxt;
}
}
The ChatSnippet model acts as a data container holding properties like unique identifier, account ID, nickname, last message content, timestamp, and read status.