Firestore获取集合内ArrayList全量数据失败 RecyclerView仅显示最后项
问题根因
该问题与Firestore数据读取逻辑无关,是RecyclerView适配器数据类型定义错误+列表数据层级嵌套错误共同导致的:
- 适配器泛型定义错误:每个RecyclerView条目应当对应单条
HolderHistItem数据,当前代码将ArrayList<HolderHistItem>作为单个列表项的数据类型,等于把整组历史记录塞给了一个item布局。 - item绑定逻辑错误:
setData方法中遍历整个历史数组反复给同一个TextView赋值,前面的赋值会被后续遍历覆盖,最终只会留下数组最后一个元素的内容。 - 数据拉取层返回结构错误:查询id匹配的
Holder文档后,将每个文档自带的holderHistoryItem数组又包装成了外层列表,返回值成了嵌套的List<ArrayList<HolderHistItem>>,和适配器的错误类型匹配后,最终只会渲染1个item、显示最后一条数据。
修复方案
1. 修正数据获取函数的返回结构
查询指定id的Holder文档时,匹配结果理论上只有1条文档,不需要把文档数组再map成嵌套列表,直接取出对应文档里的holderHistoryItem字段作为返回列表即可:
@ExperimentalCoroutinesApi fun getAllInstrumentInHolderHistory(holder: Holder): Flow<State<List<HolderHistItem>>> = callbackFlow { val snapshotListener = holderCollection .orderBy("name") .whereEqualTo("id",holder.id) .addSnapshotListener { value , error -> val response = if (error == null && value != null) { trySend(State.loading()) // 匹配指定id的Holder只会有1个文档,直接取第一条,拿出内部的历史列表 val data = value.documents.mapNotNull { doc -> doc.toObject(Holder::class.java) }.firstOrNull()?.holderHistoryItem ?: emptyList() State.success(data) } else { State.failed(error.toString()) } trySend(response) } awaitClose { snapshotListener.remove() } }
注意:对应ViewModel中持有该Flow的变量类型也要同步修改为State<List<HolderHistItem>>,保持类型一致。
2. 修正RecyclerView适配器的泛型与绑定逻辑
将适配器的单条数据类型改为HolderHistItem,去掉遍历逻辑,直接绑定单条数据到控件:
// 泛型改为单条HolderHistItem class HolderHistAdapter : ListAdapter<HolderHistItem,HolderHistAdapter.ItemHist>(ItemComparator()) { class ItemHist(view: View) : RecyclerView.ViewHolder(view) { private val b = HistitemItemBinding.bind(view) @SuppressLint("SetTextI18n") // 入参改为单条HolderHistItem,去掉遍历 fun setData(holderHistItem: HolderHistItem) = with(b){ tvListName.text = holderHistItem.instrName.toString() tvGet.text = holderHistItem.getTime.toString() tvReturn.text = holderHistItem.returnTime.toString() } companion object { fun create(parent: ViewGroup): HolderHistAdapter.ItemHist { return HolderHistAdapter.ItemHist( LayoutInflater.from(parent.context) .inflate(R.layout.histitem_item, parent, false) ) } } } class ItemComparator : DiffUtil.ItemCallback<HolderHistItem>() { override fun areItemsTheSame(oldItem: HolderHistItem, newItem: HolderHistItem): Boolean { // 用业务id判断是否是同一条,不要用hashCode return oldItem.id == newItem.id } override fun areContentsTheSame(oldItem: HolderHistItem, newItem:HolderHistItem): Boolean { return oldItem == newItem } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemHist { return ItemHist.create(parent) } override fun onBindViewHolder(holder: ItemHist, position: Int) { holder.setData(getItem(position)) } }
3. 修正页面观察数据的逻辑
适配修改后的返回值类型,直接提交拉取到的历史列表即可:
fun observHistoryItem(){ mainViewModel.getAllInstrHistoryItem(holder!!).observe(viewLifecycleOwner,{ when (it) { is State.Loading -> { // 可按需实现loading状态展示 } is State.Success -> { if(it.data.isEmpty()){ b.rcHistItem.visibility = View.GONE b.tvEmptyHist.visibility = View.VISIBLE }else { // 直接提交单条历史项组成的列表 adapterHis.submitList(it.data) b.rcHistItem.visibility = View.VISIBLE } } is State.Failed -> { Log.d("Error" , "Error name :${it.message}") } } }) }
额外提示:DiffUtil判断元素是否相同时不要用hashCode比对,用数据模型里唯一的业务id判断,避免列表刷新时出现条目错乱、不更新的问题。
内容的提问来源于stack exchange,提问作者anderson48
相关产品推荐
相关产品推荐

