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

RecyclerView所在Activity未触发OnCreate,报错“No adapter attached; skipping layout”

RecyclerView 适配问题:onCreateViewHolder未触发 + "No adapter attached; skipping layout" 报错

问题描述

我尝试用RecyclerView构建聊天列表,已创建RecyclerView和适配器,在Activity的onCreate方法中为RecyclerView设置默认值,随后调用Firebase异步接口填充数据。现在遇到两个问题:

  1. 适配器的onCreateViewHolder方法完全没触发
  2. 控制台持续报错:No adapter attached; skipping layout

相关代码

ChatAdapter 代码

class ChatAdapter(private val messages: List<MessageViewModel>, val conts: Context) : RecyclerView.Adapter<ChatAdapter.ChatViewHolder>() {

    val cont = conts

    class ChatViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val senderTextView: TextView = view.findViewById(R.id.textViewSender)
        val messageTextView: TextView = view.findViewById(R.id.textViewMessage)
        val dateTextView: TextView = view.findViewById(R.id.dateTextView)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChatViewHolder {
        val view = LayoutInflater.from(cont).inflate(R.layout.chat_view_design, parent, false)
        return ChatViewHolder(view)
    }

    @RequiresApi(Build.VERSION_CODES.O)
    override fun onBindViewHolder(holder: ChatViewHolder, position: Int) {
        val chatMessage = messages[position]
        val ref = Firebase.database.reference
        ref.child("users").child(chatMessage.fromId).get().addOnCompleteListener { task ->
            if (task.isSuccessful) {
                val snapshot = task.result
                val txt = snapshot.child("username").getValue(String::class.java)
                holder.senderTextView.text = txt
            }
        }
        holder.messageTextView.text = chatMessage.text

        val instant = Instant.ofEpochMilli((chatMessage.date * 1000).toLong())

        val formatter = DateTimeFormatter.ofPattern("MM/dd/yy")
        val newDate = Date.from(instant)

        val finalDate : LocalDate = newDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate()

        holder.dateTextView.text = formatter.format(finalDate)

        if (messages[position].fromId != Firebase.auth.currentUser?.uid) {
            holder.senderTextView.setTextColor(Color.parseColor("#FFA500"))
        }
    }

    override fun getItemCount() = messages.size
}

Firebase请求后设置适配器的代码

val adapter = ChatAdapter(messages, this)
chatAdapter = adapter
recyclerView.adapter = chatAdapter
recyclerView.scrollToPosition(messages.size - 1)
chatAdapter.notifyDataSetChanged()

Activity中初始化RecyclerView的截图

Chat Activity添加适配器

问题原因

  1. No adapter attached报错:RecyclerView在首次布局时,你还在等待Firebase异步请求返回数据,此时没有任何适配器绑定到RecyclerView,系统就会抛出这个警告。
  2. onCreateViewHolder未触发:如果直到异步请求完成才绑定适配器,可能此时RecyclerView已经完成了初始布局流程;或者初始绑定的数据源是空列表,且没有正确通知适配器更新。

解决方案

1. 提前在onCreate中绑定空适配器

不要等Firebase请求返回再绑定适配器,先初始化一个空的适配器绑定到RecyclerView,让系统知道RecyclerView有适配器可用,避免报错。

2. 改用可变数据源+适配器更新方法

把适配器的数据源改成MutableList,并添加一个更新数据的方法,这样请求完成后只需要更新数据源并通知适配器刷新,不用重新创建适配器实例,确保RecyclerView能触发onCreateViewHolder。

修改后的代码

优化后的ChatAdapter

class ChatAdapter(private val messages: MutableList<MessageViewModel>, val cont: Context) : RecyclerView.Adapter<ChatAdapter.ChatViewHolder>() {

    class ChatViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val senderTextView: TextView = view.findViewById(R.id.textViewSender)
        val messageTextView: TextView = view.findViewById(R.id.textViewMessage)
        val dateTextView: TextView = view.findViewById(R.id.dateTextView)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChatViewHolder {
        val view = LayoutInflater.from(cont).inflate(R.layout.chat_view_design, parent, false)
        return ChatViewHolder(view)
    }

    @RequiresApi(Build.VERSION_CODES.O)
    override fun onBindViewHolder(holder: ChatViewHolder, position: Int) {
        val chatMessage = messages[position]
        val ref = Firebase.database.reference
        ref.child("users").child(chatMessage.fromId).get().addOnCompleteListener { task ->
            if (task.isSuccessful) {
                val snapshot = task.result
                val txt = snapshot.child("username").getValue(String::class.java)
                holder.senderTextView.text = txt
            }
        }
        holder.messageTextView.text = chatMessage.text

        // 简化日期转换代码
        val instant = Instant.ofEpochMilli((chatMessage.date * 1000).toLong())
        val formatter = DateTimeFormatter.ofPattern("MM/dd/yy")
        val finalDate = instant.atZone(ZoneId.systemDefault()).toLocalDate()
        holder.dateTextView.text = formatter.format(finalDate)

        if (chatMessage.fromId != Firebase.auth.currentUser?.uid) {
            holder.senderTextView.setTextColor(Color.parseColor("#FFA500"))
        }
    }

    // 添加更新数据的方法
    fun updateMessages(newMessages: List<MessageViewModel>) {
        messages.clear()
        messages.addAll(newMessages)
        notifyDataSetChanged()
    }

    override fun getItemCount() = messages.size
}

Activity的onCreate修改

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_chat)

    // 初始化RecyclerView核心配置
    recyclerView = findViewById(R.id.recyclerView)
    recyclerView.layoutManager = LinearLayoutManager(this)

    // 提前绑定空适配器
    val emptyMessages = mutableListOf<MessageViewModel>()
    chatAdapter = ChatAdapter(emptyMessages, this)
    recyclerView.adapter = chatAdapter

    // 发起Firebase请求加载数据
    loadChatData()
}

private fun loadChatData() {
    // 替换成你实际的Firebase数据查询逻辑
    Firebase.database.reference.child("your_chat_messages_path")
        .addListenerForSingleValueEvent(object : ValueEventListener {
            override fun onDataChange(snapshot: DataSnapshot) {
                val loadedMessages = mutableListOf<MessageViewModel>()
                // 解析快照数据到MessageViewModel列表
                snapshot.children.forEach { child ->
                    val message = child.getValue(MessageViewModel::class.java)
                    message?.let { loadedMessages.add(it) }
                }
                // 更新适配器数据
                chatAdapter.updateMessages(loadedMessages)
                // 滚动到最新消息
                recyclerView.scrollToPosition(loadedMessages.size - 1)
            }

            override fun onCancelled(error: DatabaseError) {
                // 这里可以加请求失败的提示处理
            }
        })
}

额外优化建议

  • 缓存用户信息:当前onBindViewHolder中每条消息都发起一次Firebase用户查询,会导致大量重复请求。建议在加载聊天消息时,批量获取所有涉及的用户ID对应的用户名,存入Map<String, String>缓存,在onBindViewHolder中直接从缓存取,提升性能。
  • 使用DiffUtil:如果聊天消息量大,用DiffUtil代替notifyDataSetChanged()可以减少不必要的UI刷新,提升流畅度。

内容的提问来源于stack exchange,提问作者Ron Jones Jr

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 11:29:52