You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 01:03:11