自定义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
相关产品推荐
相关产品推荐

