如何在RecyclerView的聊天消息组上方显示日期时间?
解决方案:在聊天RecyclerView中按时间分组显示日期头
要实现按时间分组显示日期头,需要从数据源处理、Adapter视图类型判断和数据绑定三个方面修改现有代码,具体步骤如下:
1. 创建混合类型的列表项密封类
首先定义一个密封类,区分日期头和普通消息,让Adapter能根据类型返回不同视图:
sealed class ChatItem { // 普通消息项 data class Message(val data: SingleChatData) : ChatItem() // 日期头项 data class DateHeader(val dateString: String) : ChatItem() }
2. 修改Adapter数据源处理逻辑
将原始的SingleChatData列表转换为包含日期头的ChatItem列表,遍历消息时判断当前消息与上一条是否属于同一天,不同则插入日期头:
class SingleChatAdapter(private var context: Context, messageList: ArrayList<SingleChatData>?, senderRoom: String, receiverRoom: String, private val incDecNum: (Boolean) -> Unit, private val showChosenToolbar: (Boolean, Boolean) -> Unit): RecyclerView.Adapter<ViewHolder?>() { private var selectedShared = IsSelectedShared(context) private val sentNum = 1 private val receivedNum = 2 private val msgContainer = 3 private val senderRoom: String private val receiverRoom: String private lateinit var chatItems: ArrayList<ChatItem> // 修改为ChatItem类型列表 private val itemsData: ArrayList<SelectedItemsData>?= null // ... 其他原有代码 init { if (messageList != null) { chatItems = ArrayList() var previousDate: String? = null // 用于判断是否为同一天的格式化器 val dateComparator = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) for (msg in messageList) { // 假设SingleChatData有timestamp字段(Long类型,存储消息时间戳) val currentDate = dateComparator.format(Date(msg.timestamp)) if (currentDate != previousDate) { // 转换为友好显示的日期文本(如"今天"、"昨天"、"2024年5月20日") val friendlyDate = getFriendlyDate(msg.timestamp) chatItems.add(ChatItem.DateHeader(friendlyDate)) previousDate = currentDate } chatItems.add(ChatItem.Message(msg)) } } this.senderRoom = senderRoom this.receiverRoom = receiverRoom } // 转换时间戳为友好日期字符串的辅助方法 private fun getFriendlyDate(timestamp: Long): String { val currentTime = System.currentTimeMillis() val msgDate = Date(timestamp) val msgCalendar = Calendar.getInstance().apply { time = msgDate } val currentCalendar = Calendar.getInstance().apply { time = Date(currentTime) } return when { // 今天的消息 msgCalendar.get(Calendar.YEAR) == currentCalendar.get(Calendar.YEAR) && msgCalendar.get(Calendar.DAY_OF_YEAR) == currentCalendar.get(Calendar.DAY_OF_YEAR) -> "今天" // 昨天的消息 msgCalendar.get(Calendar.YEAR) == currentCalendar.get(Calendar.YEAR) && msgCalendar.get(Calendar.DAY_OF_YEAR) == currentCalendar.get(Calendar.DAY_OF_YEAR) - 1 -> "昨天" // 今年内的消息 msgCalendar.get(Calendar.YEAR) == currentCalendar.get(Calendar.YEAR) -> SimpleDateFormat("MM月dd日", Locale.getDefault()).format(msgDate) // 往年的消息 else -> SimpleDateFormat("yyyy年MM月dd日", Locale.getDefault()).format(msgDate) } } }
3. 修改Adapter核心方法
3.1 调整getItemCount
返回混合列表的总长度:
override fun getItemCount(): Int { return chatItems.size }
3.2 修改getItemViewType
根据ChatItem的类型返回对应的视图类型:
override fun getItemViewType(position: Int): Int { return when (val item = chatItems[position]) { is ChatItem.DateHeader -> msgContainer is ChatItem.Message -> { val newMsg = item.data if (FirebaseAuth.getInstance().uid == newMsg.senderId) sentNum else receivedNum } } }
3.3 完善onBindViewHolder
分别处理日期头和消息项的绑定逻辑:
override fun onBindViewHolder(holder: ViewHolder, position: Int) { val item = chatItems[position] when (holder) { // 绑定日期头 is ContainerMsgViewHolder -> { val dateItem = item as ChatItem.DateHeader holder.binding.containerDay.text = dateItem.dateString } // 绑定发送消息 is SentMsgViewHolder -> { val msgItem = item as ChatItem.Message val msg = msgItem.data // 根据你的数据结构绑定消息内容,示例: holder.binding.sentMsgText.text = msg.message // 添加消息时间显示 val timeFormat = SimpleDateFormat("HH:mm", Locale.getDefault()) holder.binding.sentMsgTime.text = timeFormat.format(Date(msg.timestamp)) } // 绑定接收消息 is ReceivedMsgViewHolder -> { val msgItem = item as ChatItem.Message val msg = msgItem.data holder.binding.receivedMsgText.text = msg.message val timeFormat = SimpleDateFormat("HH:mm", Locale.getDefault()) holder.binding.receivedMsgTime.text = timeFormat.format(Date(msg.timestamp)) } } }
4. 调整日期头布局
修改msg_group_container.xml的根布局高度为wrap_content,避免日期头占据过多空间:
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" <!-- 从match_parent改为wrap_content --> xmlns:app="http://schemas.android.com/apk/res-auto"> <!-- 原有CardView和TextView代码不变 --> </androidx.constraintlayout.widget.ConstraintLayout>
注意事项
- 确保
SingleChatData类包含timestamp字段(Long类型),用于存储消息的时间戳。 - 如果使用Java 8+的日期API,可以替换
SimpleDateFormat和Calendar为更安全的DateTimeFormatter和LocalDate。
内容的提问来源于stack exchange,提问作者Mahmoud Nabil
相关产品推荐
相关产品推荐

