RecycleView для корпоративного мессенджера
Пишу корпоративный мессенджер по типу WhatApp'а, вернее клиентскую его часть на Android..
Для отображении сообщений в чате использую стандартный RecycleView, но с разными типами ViewHolder, так как сообщения могут быть разные (входящие, исходящие, удаленные, системный текст и т.д.).
С этим RecycleView у меня постоянные проблемы:
При скролле элементы могут перемешиваться в каком-то случайном порядке. Помогает setRecycle(false).
Если исполнить пункт 1, то нельзя изменять элемент, если сообщение удаляется, то старый View остается на месте, так как его как-бы нельзя удалить
Перемещение к нужному сообщению в чате по позиции (например к закрепленному сообщению) происходит только по .smoothScrollToPosition(), что приводит к порождению событий onScroll, которые я отслеживаю и там своя логика. Мне не надо отслеживать системные перемещения по чату, только пользовательские, но их никак не различить. В общем хотелось бы просто переместиться к позиции в RecycleView без скроллов.
Скролл к последней позиции также происходит не всегда точно, пол сообщения (если одно длинное) остается не доскроленным.
Также хотелось бы получить binding именно на добавление и удаление элементов в RecycleView, то есть имея какой либо Observable<List<?> list>, который привязана к View и при изменении самого list, view тоже реагировала на это, без всяких notifyDataSetChanged
Крайний пункт не самый важный, а остальные меня убивают...
Может быть есть какой-нибудь альтернативные RecycleView, может быть вообще посоветуете что-нибудь другое?
Заранее, спасибо.
Код адаптера
class MessagesAdapter (
private val messages: MutableList<ChatMessageWrapper>,
private val chatMessagePopupMenuListener: ChatMessageActionListener
) : RecyclerView.Adapter<AbstractMessageHolder>() {
override fun getItemViewType(position: Int): Int {
return messages[position].type.value
}
fun getMessages() : MutableList<ChatMessageWrapper> {
return messages
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AbstractMessageHolder {
when(ChatMessageType.fromInt(viewType)){
ChatMessageType.SENT -> {
val root = LayoutInflater.from(parent.context).inflate(R.layout.chat_message_send_item, parent,false)
return SentMessageViewHolder(root, chatMessagePopupMenuListener)
}
ChatMessageType.RECEIVED -> {
val root = LayoutInflater.from(parent.context).inflate(R.layout.chat_message_received_item, parent,false)
return ReceivedMessageViewHolder(root, chatMessagePopupMenuListener)
}
ChatMessageType.DELETED_SENT -> {
val root = LayoutInflater.from(parent.context).inflate(R.layout.chat_message_send_deleted_item, parent,false)
return DeletedMessageViewHolder(root)
}
ChatMessageType.DELETED_RECEIVED -> {
val root = LayoutInflater.from(parent.context).inflate(R.layout.chat_message_received_deleted_item, parent,false)
return DeletedMessageViewHolder(root)
}
ChatMessageType.SIMPLE_TEXT -> {
val root = LayoutInflater.from(parent.context).inflate(R.layout.chat_message_simple_text, parent,false)
return SimpleTextMessageViewHolder(root)
}
ChatMessageType.UNKNOWN -> {
throw IllegalStateException("Unknown type of chat item message")
}
}
}
fun appendMessage(item: ChatMessageWrapper) {
var indexOfMsg = -1
if (item.message is InteractionMessagePacket) {
indexOfMsg = findIndexOfMessage(item.message.id)
}
if (indexOfMsg < 0) {
messages.add(item)
notifyItemInserted(messages.size)
} else {
messages[indexOfMsg] = item
notifyItemChanged(indexOfMsg)
}
}
fun appendNewMessagesPack(wrappedMessages: MutableList<ChatMessageWrapper>) {
messages.addAll(0, wrappedMessages)
notifyItemRangeInserted(0, wrappedMessages.size)
}
fun findIndexOfMessage(itemId: String) : Int {
val msgInView : ChatMessageWrapper
try {
msgInView = messages.filter{it.message is InteractionMessagePacket}.first { (it.message as InteractionMessagePacket).id == itemId }
} catch (ex: NoSuchElementException) {
return -1
}
return messages.indexOf(msgInView)
}
override fun onBindViewHolder(holder: AbstractMessageHolder, position: Int) {
val item = messages[position]
holder.setItem(item.message)
holder.bind()
holder.setIsRecyclable(false)
}
override fun getItemCount(): Int {
return messages.size
}
}