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

自定义onItemClicked的FirestoreRecyclerAdapter触发索引越界异常

问题解决:FirestoreRecyclerAdapter动态更新后点击新增项抛出IndexOutOfBoundsException

问题根源

你在Activity里维护了独立的userList和groupsList存储聊天数据,但这两个列表只在初始化时通过单次查询加载。当FirestoreRecyclerAdapter因云端数据变化动态更新列表时,本地列表不会同步更新,导致点击新增项时,position超出了本地列表的长度,触发索引越界异常。

解决方案

直接从FirestoreRecyclerAdapter中获取当前项的UserProfile和对应文档ID(即roomId),不再依赖本地维护的列表,确保数据与适配器完全同步。

1. 修改适配器代码

调整点击回调接口,直接传递目标用户数据和roomId,同时通过snapshots.getSnapshot(position).id获取文档ID:

class ChatListAdapter(options: FirestoreRecyclerOptions<UserProfile>
    ) : FirestoreRecyclerAdapter<UserProfile, ChatListAdapter.UserHolder>(options) {
    private var clickListener: ClickListener? = null

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChatListAdapter.UserHolder {
        val view = LayoutInflater.from(parent.context).inflate(
            android.R.layout.simple_list_item_1,
            parent, false
        )
        return UserHolder(view)
    }

    override fun onBindViewHolder(holder: UserHolder, position: Int, model: UserProfile) {
        holder.bind(model)
        holder.itemView.setOnClickListener {
            // 获取当前项对应的Firestore文档ID(即roomId)
            val roomId = snapshots.getSnapshot(position).id
            clickListener?.onItemClicked(model, roomId)
        }
    }

    inner class UserHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        private var userNameText: TextView = itemView.findViewById(android.R.id.text1)

        fun bind(user: UserProfile) {
            userNameText.text = user.userName
        }
    }

    fun setOnItemClickListener(clickListener: ClickListener) {
        this.clickListener = clickListener
    }

    // 修改回调接口,直接传递所需数据
    interface ClickListener {
        fun onItemClicked(user: UserProfile, roomId: String)
    }
}

2. 修改Activity代码

移除本地维护的userList和groupsList,直接使用适配器回调传递的数据启动聊天页面:

val query = fsDb.collection("groups")
    .document(userId!!).collection("userGroups")
val options = FirestoreRecyclerOptions.Builder<UserProfile>()
    .setQuery(query, UserProfile::class.java).build()

chatListAdapter = ChatListAdapter(options)
chatsRecyclerView.layoutManager = LinearLayoutManager(this)
chatsRecyclerView.itemAnimator = null
chatListAdapter!!.startListening()
chatsRecyclerView.adapter = chatListAdapter

val uIRef = fsDb.collection("users").document(userId!!)
uIRef.get().addOnCompleteListener {
    if (it.isSuccessful) {
        val doc = it.result
        if (doc.exists()) {
            val fromUser = doc.toObject(UserProfile::class.java)!!

            // 直接设置适配器点击监听,无需重复查询groups集合
            chatListAdapter!!.setOnItemClickListener(object : ChatListAdapter.ClickListener {
                override fun onItemClicked(toUser: UserProfile, roomId: String) {
                    val intent = Intent(this@ChatListActivity, ChatActivity::class.java)
                    intent.putExtra("fromUser", fromUser)
                    intent.putExtra("toUser", toUser)
                    intent.putExtra("roomId", roomId)
                    startActivity(intent)
                }
            })
        }
    }
}

说明

修改后,不管适配器如何动态更新列表,点击项时都会直接从适配器的最新快照中获取对应的数据和文档ID,完全适配列表的实时状态,彻底解决索引越界问题,同时减少了一次冗余的Firestore查询。

内容的提问来源于stack exchange,提问作者Trevor Bonas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 08:45:39