Message List View

LAST EDIT Feb 17 2021

MessageListView renders a list of messages. It implements all the features you expect from a chat:

  • Displaying text, images, and attachments

  • Rich URL previews

  • Editing, copying, flagging, and deleting messages

  • Opening and creating threads

  • Reactions

  • Read state

  • Replies

  • Muting and blocking users

MessageListView in light mode

MessageListView, like our other components, supports dark mode:

MessageListView in dark mode

The example below shows how to use the MessageListView in your layout:

1
2
3
4
<io.getstream.chat.android.ui.message.list.MessageListView 
    android:id="@+id/messageList" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" />

Displaying new messagesCopied!

To display new messages in the view, use displayNewMessages:

1
2
3
4
5
6
7
8
val messageItem = MessageListItem.MessageItem( 
    message = Message(text = "Lorem ipsum dolor"), 
    positions = listOf(MessageListItem.Position.TOP), 
    isMine = true 
) 
 
val messageItemListWrapper = MessageListItemWrapper(listOf(messageItem)) 
binding.messageListView.displayNewMessages(messageItemListWrapper)
1
2
3
4
5
6
7
8
9
10
11
Message message = new Message(); 
message.setText("Lorem ipsum dolor"); 
MessageListItem.MessageItem messageItem = new MessageListItem.MessageItem( 
    message, new ArrayList<>(), true, new ArrayList<>(), false, false 
); 
 
MessageListItemWrapper messageWrapper = new MessageListItemWrapper( 
    Collections.singletonList(messageItem), false, false, false 
); 
 
binding.messageListView.displayNewMessages(messageWrapper);

This piece of code will show a simple message in the chat. One way to avoid handling the logic of getting new messages and calling displayNewMessages manually, it is possible to use the MessageListViewModel, as described in the next section.

Showing empty stateCopied!

Show empty state:

1
2
3
fun emptyState() {         
    messageListView.showEmptyStateView() 
}
1
2
3
public void emptyState() { 
    messageListView.showEmptyStateView(); 
}

Showing loading stateCopied!

Show loading state:

1
messageListView.showLoadingView()
1
2
//When loading information, show loading view 
messageListView.showLoadingView();

Binding with a ViewModelCopied!

To handle the implementation for receiving new messages, showing loading state, pagination... and many other common behaviours, it is recommended to use MessageListViewModel. The following code shows how to connect the view and the ViewModel using bindView:

1
2
3
4
5
// Get ViewModel 
val factory: MessageListViewModelFactory = MessageListViewModelFactory(cid = "channelType:channelId") 
val viewModel: MessageInputViewModel by viewModels { factory } 
// Bind it with MessageInputView 
viewModel.bindView(messageInputView, viewLifecycleOwner)
1
2
3
4
5
6
7
// Get ViewModel 
MessageListViewModelFactory factory = new MessageListViewModelFactory("channelType:channelId"); 
MessageListViewModel viewModel = 
        new ViewModelProvider(this, factory).get(MessageListViewModel.class); 
 
// Bind it with MessageListView 
MessageListViewModelBinding.bind(viewModel, messageListView, getViewLifecycleOwner());

This will set the listeners and handlers on the message list, forwarding everything to the ViewModel. It is also possible to set these manually: the options for listeners and handlers are described in the next sections.

ListenersCopied!

MessageListView provides multiple methods for handling user interactions handling, that includes different clicks:

1
2
3
4
5
6
7
8
9
messageListView.setMessageClickListener { message -> 
    // Handle click on message 
} 
messageListView.setMessageLongClickListener { message -> 
    // Handle long click on message 
} 
messageListView.setAttachmentClickListener { message, attachment -> 
    // Handle long click on attachment 
}
1
2
3
4
5
6
7
8
9
messageListView.setMessageClickListener((message) -> { 
    // Handle click on message 
}); 
messageListView.setMessageLongClickListener((message) -> { 
    // Handle long click on message 
}); 
messageListView.setAttachmentClickListener((message, attachment) -> { 
    // Handle click on attachment 
});

A full list of available listeners to set can be found here.

HandlersCopied!

It is also possible to change general behaviour by using handlers. For example:

1
2
3
4
5
6
7
8
9
messageListView.setMessageEditHandler { message -> 
    // Handle edit message 
} 
messageListView.setMessageDeleteHandler { message -> 
    // Handle delete message 
} 
messageListView.setAttachmentDownloadHandler { attachment -> 
    // Handle attachment download 
}
1
2
3
4
5
6
7
8
9
messageListView.setMessageEditHandler((message) -> { 
    // Handle edit message 
}); 
messageListView.setMessageDeleteHandler((message) -> { 
    // Handle delete message 
}); 
messageListView.setAttachmentDownloadHandler((attachment) -> { 
    // Handle attachment download 
});

The full list of available handlers is available here.

CustomizationsCopied!

Many properties about this view, like colours, padding and margins, can be changed via XML, the list of attributes can be found here.

Some other customizations can be done programmatically. Like:

New Messages BehaviourCopied!

It is possible to have 2 behaviours for when new messages arrive, scroll to the bottom of the screen when a new message arrives (SCROLL_TO_BOTTOM), or count the number of new messages that arrived (COUNT_UPDATE). They can be set using the XML, as described in the section above, or using setNewMessagesBehaviour:

1
2
3
fun setNewMessageBehaviour() { 
    messageListView.setNewMessagesBehaviour(MessageListView.NewMessagesBehaviour.COUNT_UPDATE) 
}
1
2
3
4
5
public void setNewMessageBehaviour() { 
    messageListView.setNewMessagesBehaviour( 
        MessageListView.NewMessagesBehaviour.COUNT_UPDATE 
    ); 
}

Date time formatterCopied!

It is necessary to implement the interface com.getstream.sdk.chat.utils.DateFormatter:

1
2
3
4
5
6
7
8
9
10
11
12
13
messageListView.setMessageDateFormatter( 
    object : DateFormatter { 
        override fun formatDate(localDateTime: LocalDateTime?): String { 
            // Provide a way to format Date 
            return DateTimeFormatter.ofPattern("dd/MM/yyyy").format(localDateTime) 
        } 
 
        override fun formatTime(localTime: LocalTime?): String { 
            // Provide a way to format Time. 
           return DateTimeFormatter.ofPattern("HH:mm").format(localTime) 
       } 
   } 
)
1
2
3
4
5
6
7
8
9
10
11
12
13
messageListView.setMessageDateFormatter(new DateFormatter() { 
    @NotNull 
    @Override 
    public String formatDate(@Nullable LocalDateTime localDateTime) { 
        return DateTimeFormatter.ofPattern("dd/MM/yyyy").format(localDateTime); 
    } 
 
    @NotNull 
    @Override 
   public String formatTime(@Nullable LocalTime localTime) { 
       return DateTimeFormatter.ofPattern("HH:mm").format(localTime); 
   } 
});

Result:

Format of HH:mm

MessageListItemViewHolderFactoryCopied!

To change the way messages are presented to the user, it is necessary to set the MessageListItemViewHolderFactory, which will provide a different kind of messages accordingly to the message type:

1
2
3
4
5
6
7
8
9
10
11
12
class MessageListItemViewHolderFactoryExtended : MessageListItemViewHolderFactory() { 
    override fun createViewHolder( 
        parentView: ViewGroup, 
        viewType: Int, 
    ): BaseMessageItemViewHolder<out MessageListItem> { 
        // Create a new type of view holder here, if needed 
        return super.createViewHolder(parentView, viewType) 
    } 
} 
 
val factory: MessageListItemViewHolderFactory = MessageListItemViewHolderFactoryExtended() 
messageListView.setMessageViewHolderFactory(factory)
1
2
3
4
5
6
7
8
9
10
11
class MessageListItemViewHolderFactoryExtended extends MessageListItemViewHolderFactory { 
    @NotNull 
    @Override 
    public BaseMessageItemViewHolder<? extends MessageListItem> createViewHolder(@NotNull ViewGroup parentView, int viewType) { 
        // Create a new type of view holder here, if needed 
        return super.createViewHolder(parentView, viewType); 
    } 
} 
 
MessageListItemViewHolderFactory factory = new MessageListItemViewHolderFactoryExtended(); 
messageListView.setMessageViewHolderFactory(factory);

Filter messagesCopied!

It is possible to filter messages. The predicate must be set using setMessageListItemPredicate, and the messages will be filtered once they arrive:

1
2
3
4
messageListView.setMessageListItemPredicate { messageList -> 
    // Boolean logic here 
    true 
}
1
2
3
4
messageListView.setMessageListItemPredicate(messageList -> { 
    // Boolean logic here 
    return true; 
});

Changing behaviour for end region reachedCopied!

It is possible to set a listener for when the end region was reached:

1
2
3
4
5
6
7
messageListView.setEndRegionReachedHandler { 
    // Handle pagination and include new logic 
 
    // Option to log the event and use the viewModel 
    viewModel.onEvent(MessageListViewModel.Event.EndRegionReached) 
    Log.e("LogTag", "On load more") 
}
1
2
3
4
5
6
7
messageListView.setEndRegionReachedHandler(() -> { 
    // Handle pagination and include new logic 
 
    // Option to log the event and use the viewModel 
    viewModel.onEvent(MessageListViewModel.Event.EndRegionReached.INSTANCE); 
    Log.e("LogTag", "On load more"); 
});

NOTE: Please be aware that if you're using the ViewModel and override the EndRegionReachedHandler you have to handle pagination, otherwise pagination won't work.