DialogsList is a component for displaying and management of a dialog list. The main features: fast and simple implementation, one-on-one and group dialog support, unread messages’ markers. Also there are several customization methods for changing appearance of dialog list and realization of additional features, which are not included by default.
To start using the component, you just need to follow a few steps:
<com.stfalcon.chatkit.dialogs.DialogsList
android:id="@+id/dialogsList"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
For DialogsList
default realization you just need to transfer dialog list and dialog avatar image upload realization to adapter’s constructor (Click here to know more about image loading).
DialogsListAdapter dialogsListAdapter = new DialogsListAdapter<>(dialogs, new ImageLoader() {
@Override
public void loadImage(ImageView imageView, String url) {
//If you using another library - write here your way to load image
Picasso.with(DialogsListActivity.this).load(url).into(imageView);
}
});
dialogsListView.setAdapter(dialogsListAdapter);
To be able to add dialog, you must implement the IDialog
interface to your existing model and override its methods:
public class DefaultDialog implements IDialog {
/*...*/
@Override
public String getId() {
return id;
}
@Override
public String getDialogPhoto() {
return dialogPhoto;
}
@Override
public String getDialogName() {
return dialogName;
}
@Override
public ArrayList<IUser> getUsers() {
return users;
}
@Override
public IMessage getLastMessage() {
return lastMessage;
}
@Override
public void setLastMessage(IMessage lastMessage) {
this.lastMessage = lastMessage;
}
@Override
public int getUnreadCount() {
return unreadCount;
}
As you can see, you need also to implement the IUser
interface for message author:
public class Author implements IUser {
/*...*/
@Override
public String getId() {
return id;
}
@Override
public String getName() {
return name;
}
@Override
public String getAvatar() {
return avatar;
}
}
That's all! This approach allows the adapter to work with your dialog model without any converting of types!
When your models are ready to be used by adapter, you can simply add them to the list. There is four ways to do this:
- adapter.setItems(List items) - replaces existing list with a new dialog list;
- adapter.addItems(List items) - adds a new dialog list to the end of the list;
- adapter.addItem(DIALOG dialog) - adds one dialog to the end of the list
- adapter.addItem(int position, DIALOG dialog) - adds a new dialog to the specified position.
If dialog has changed, you can update it by position in list by calling adapter.updateItem(int position, DIALOG item)
or update it by dialog id by calling adapter.updateItemById(DIALOG item)
There’s also a method provided to update a last dialog message. To do so, you need to call adapter.updateDialogWithMessage(String dialogId, IMessage message)
. This method returns true
, if a dialog with given id is in the list and it’s updated, and “false”, if a dialog is not found. It allows to provide an opportunity to create a new dialog, if it’s not in the list. For example:
private void onNewMessage(String dialogId, IMessage message) {
if (!dialogsListAdapter.updateDialogWithMessage(dialogId, message)) {
//Dialog with this ID doesn't exist, so you can create new Dialog or reload all dialogs list
}
}
To delete messages from the list, you need to call adapter.deleteById(String id)
.
To delete all of the dialogs, just call adapter.clear()
method.
Of course, the adapter have listeners for such important actions as short and long clicks. They just returns a message object that has been pressed, with a type that is specified as the generic type of adapter:
dialogsListAdapter.setOnDialogClickListener(new DialogsListAdapter.OnDialogClickListener<Dialog>() {
@Override
public void onDialogClick(Dialog dialog) {
//On item click action
}
});
dialogsListAdapter.setOnDialogLongClickListener(new DialogsListAdapter.OnDialogLongClickListener<Dialog>() {
@Override
public void onDialogLongClick(Dialog dialog) {
//on long item click action
}
});
Certainly, initially defined appearance will not be suitable for everybody. And again, we have several options to solve this problem.
By default, the date in the dialogs is specified in the HH: mm
format , but this does not satisfy all requirements. For example, you need to display such common options as "Today", "Yesterday", "Last week" etc. To achieve this we have the DateFormatter.Formatter
interface:
@Override
public String format(Date date) {
if (DateFormatter.isToday(date)) {
return DateFormatter.format(date, DateFormatter.Template.TIME);
} else if (DateFormatter.isYesterday(date)) {
return getString(R.string.date_header_yesterday);
…...
} else {
return DateFormatter.format(date, DateFormatter.Template.STRING_DAY_MONTH_YEAR);
}
}
After creating it, you can pass it to adapter:
dialogsAdapter.setDatesFormatter(this);
...and get this kind of result:
The simplest way to customize appearance is to use DialogsList
widget attributes. With them we can set elements separately for read and unread dialogs: element’ background color, font size, color and text style for a dialog title, a last message, a date of last message. And also for dialog’s avatar size and last message author’s avatar size (only for group chats), setting a separator between elements (appearance, color, indents).
For example, when you define attributes like this:
<com.stfalcon.chatkit.dialogs.DialogsList
android:id="@+id/dialogsList"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:dialogDividerLeftPadding="0dp"
app:dialogMessageTextSize="17sp"
app:dialogTitleTextStyle="bold"
app:dialogUnreadBubbleBackgroundColor="@color/red"
app:dialogUnreadItemBackground="@color/gray_transparent"
app:dialogUnreadTitleTextStyle="bold"/>
...you can get this:
You can view all available attributes here
But what if changing appearance is not enough? Not a problem, create your own layout! The only condition: IDs must match to default IDs, and widget types should not cause ClassCastException (they must be either the same or a subclass type). Here are IDs with types, defined in DialogsList:
@id/dialogRootLayout
(ViewGroup)@id/dialogContainer
(ViewGroup)@id/dialogName
(TextView)@id/dialogDate
(TextView)@id/dialogLastMessage
(TextView)@id/dialogUnreadBubble
(TextView)@id/dialogLastMessageUserAvatar
(ImageView)@id/dialogAvatar
(ImageView)@id/dialogDividerContainer
(ViewGroup)@id/dialogDivider
(View)
For better understanding see how custom layout looks like.
After creating layout you need to transfer it to the adapter’s constructor.
dialogsListAdapter = new DialogsListAdapter<>(R.layout.item_dialog_custom, imageLoader);
Sometimes you can’t get it through with the default dialog elements. For example, you need to display a user online status indicator. So, you definitely need to create your own layout, and you still need to change ViewHolder logic.
To create your holder, you need to inherit your class from DialogsListAdapter.BaseDialogViewHolder<>
, and transfer your message class to generic type, because on the assumption of it the onBind(IDialog dialog)
method will be typified. This method is similar to onBindViewHolder()
method from RecyclerView.Adapter
class: you can manipulate your data from it and upload images through protected
of the ImageLoader
field.
However, if you’re going to add new features without rewriting behavior from a scratch, you can inherit a class from a standard holder realization: DialogViewHolder
.
For example, you can add user online status indicator in one-on-one dialog by doing this:
public class CustomDialogViewHolder extends DialogsListAdapter.DialogViewHolder<DefaultDialog> {
private View onlineView;
public CustomDialogViewHolder(View itemView) {
super(itemView);
onlineView = itemView.findViewById(R.id.online);
}
@Override
public void onBind(DefaultDialog dialog) {
super.onBind(dialog);
//if not a group dialog, hide indicator
if (dialog.getUsers().size() > 1) {
onlineView.setVisibility(View.GONE);
} else {
//if not a group dialog, hide indicator
boolean isOnline = ((DefaultUser)dialog.getUsers().get(0)).isOnline();
onlineView.setVisibility(View.VISIBLE);
if (isOnline) {
onlineView.setBackgroundResource(R.drawable.shape_bubble_online);
} else {
onlineView.setBackgroundResource(R.drawable.shape_bubble_offline);
}
}
}
}