如何在RecyclerView的两类消息Item之间插入日期头布局?
聊天列表插入日期头布局的问题修复方案
核心问题梳理
- 第一个错误:
onCreateViewHolder参数使用错误。该方法的第二个输入参数是getItemViewType返回的视图类型值,不是列表项位置position,你当前的判断逻辑完全错配,导致视图加载混乱。 - 第二个错误:
getItemViewType逻辑冲突。你现在的逻辑直接用原有消息的位置返回日期视图类型,相当于把原有的消息项直接替换成了日期头,自然会丢失消息内容。 - 第三个错误:日期判断逻辑反向。你现在判断当前消息日期和上一条相等时返回日期类型,实际应该是当前消息日期和上一条不相等的时候才需要显示日期头。
- 第四个错误:数据源没有对应日期项的占位,你没有在消息列表中插入额外的日期数据,直接复用消息的位置返回日期类型,必然会覆盖原有消息。
修复方案(两种可选)
方案1:不修改数据源,在消息布局中内置日期控件
这种方案不需要调整列表结构,实现最简单:
- 给
item_outgoing_message和item_incoming_message布局的顶部都加一个日期TextView,默认设为GONE隐藏 - 删掉你原来加的
item_show_time_chat布局和相关视图类型判断,getItemViewType只保留原来的0和1两种类型,区分接收/发送消息 - 在
onBindViewHolder中加判断:
// 第一条消息永远显示日期 if (position == 0) { holder.showDateTv?.visibility = View.VISIBLE holder.showDateTv?.text = mChatList[position].getMessageDate() } else { val currentDate = mChatList[position].getMessageDate() val lastDate = mChatList[position-1].getMessageDate() // 当前日期和上一条不一样时显示日期 if (currentDate != lastDate) { holder.showDateTv?.visibility = View.VISIBLE holder.showDateTv?.text = currentDate } else { holder.showDateTv?.visibility = View.GONE } }
方案2:修改数据源,插入独立的日期项
如果你需要日期头作为完全独立的列表项,用这个方案:
- 先给聊天数据封装通用类型,可以用密封类:
sealed class ChatItem { data class MessageItem(val chat: Chat) : ChatItem() data class DateItem(val date: String) : ChatItem() }
- 初始化列表的时候,对原始消息列表做处理,插入日期项:
val chatItems = mutableListOf<ChatItem>() var lastDate = "" mChatList.forEach { chat -> val currentDate = chat.getMessageDate() if (currentDate != lastDate) { chatItems.add(ChatItem.DateItem(currentDate)) lastDate = currentDate } chatItems.add(ChatItem.MessageItem(chat)) } // 把处理后的chatItems作为Adapter的数据源
- 调整
getItemViewType逻辑:
override fun getItemViewType(position: Int): Int { return when(chatItems[position]) { is ChatItem.MessageItem -> { if((chatItems[position] as ChatItem.MessageItem).chat.getSender() == currentUserID) 1 else 0 } is ChatItem.DateItem -> 2 } }
- 修正
onCreateViewHolder的参数和判断逻辑:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { return when(viewType) { 1 -> { val view = LayoutInflater.from(mContext).inflate(R.layout.item_outgoing_message, parent, false) MessageViewHolder(view) } 0 -> { val view = LayoutInflater.from(mContext).inflate(R.layout.item_incoming_message, parent, false) MessageViewHolder(view) } else -> { val view = LayoutInflater.from(mContext).inflate(R.layout.item_show_time_chat, parent, false) DateViewHolder(view) } } }
onBindViewHolder根据类型分别绑定即可。
内容的提问来源于stack exchange,提问作者mr. groot
相关产品推荐
相关产品推荐

