You need to enable JavaScript to run this app. 如何在RecyclerView中通过HashMap从Firebase取值 解决列表项重复显示问题
优惠活动
大模型
产品
解决方案
定价
更多

如何在RecyclerView中通过HashMap从Firebase取值 解决列表项重复显示问题

现有实现的核心问题

出现所有列表项重复展示最后一条记录的现象,是三个逻辑错误共同导致的:

  • 完全误解FirebaseRecyclerAdapter的工作机制:该适配器本身已内置全量数据拉取、数据变更监听、列表位置与数据节点映射的能力,无需在onBindViewHolder中额外手动注册全量数据监听。
  • 冗余的全量拉取逻辑是内容重复的直接诱因:每绑定一个列表项的Holder,就会触发一次全量查询,查询返回的所有记录会在循环中把字段值挨个设置给当前Holder,循环执行结束后Holder最终只会保留最后一条记录的内容。列表有多少个项,就会重复执行多少次这个全量覆盖流程,最终所有Holder都会被覆盖成最后一条记录的内容,和实际运行表现完全一致。
  • 适配逻辑存在断层:目标是跳过实体类、用HashMap作为数据载体,但当前适配器的泛型仍硬绑定了RiderModel.class,既未利用SDK自带的HashMap反序列化能力,又额外手动重复实现拉值逻辑,无任何收益还增加了性能损耗。
正确改造方案

核心调整逻辑

直接利用Firebase SDK原生支持的HashMap反序列化能力,完全移除手动拉数据的冗余代码,调整步骤如下:

  1. 删除onBindViewHolder中所有query.addListenerForSingleValueEvent相关的全量拉取逻辑、空的HashMap遍历逻辑、成员变量里定义的空arrayList。
  2. 把适配器和FirebaseRecyclerOptions的泛型从RiderModel改成HashMap,利用SDK自动把每个Trip节点的内容反序列化成对应位置的HashMap对象,不需要手动拆值。
  3. 直接从每个位置传入的HashMap对象中取对应字段值绑定到Holder上,避免全量遍历造成的Holder内容覆盖。

改造后核心代码示例

private void FetchHistoryInformation() {
    String user = FirebaseAuth.getInstance().getCurrentUser().getUid();
    DatabaseReference tripRef = FirebaseDatabase.getInstance().getReference(Common.RIDER_INFO_REFERENCE).child(user).child("Trips");
    Query query = tripRef.orderByChild("tripCount");

    // 直接绑定HashMap作为数据类型,跳过实体类
    FirebaseRecyclerOptions<HashMap<String, Object>> options = new FirebaseRecyclerOptions.Builder<HashMap<String, Object>>()
            .setLifecycleOwner(this)
            .setQuery(query, (Class<HashMap<String, Object>>)(Class<?>)HashMap.class)
            .build();

    adapter = new FirebaseRecyclerAdapter<HashMap<String, Object>, CompletedTripAdapter>(options) {
        @Override
        protected void onBindViewHolder(@NonNull CompletedTripAdapter holder, int position, @NonNull HashMap<String, Object> model) {
            // 直接从当前位置对应的HashMap取值,无全量遍历
            String pickup = (String) model.get("pickupLocation");
            String destination = (String) model.get("destination");
            Long timeStamp = (Long) model.get("timeStamp");

            holder.txt_pickup.setText(pickup);
            holder.txt_destination.setText(destination);
            holder.txt_trip_status.setText(R.string.status_complete);
            holder.txt_trip_date.setText(UserUtils.getDate(timeStamp));

            // 点击事件直接取当前位置的数据库Key,避免位置错位
            holder.setItemClickListerner((view, pos) -> {
                String tripKey = getRef(position).getKey();
                Toast.makeText(getActivity(), tripKey, Toast.LENGTH_SHORT).show();
                Intent intent = new Intent(getActivity(), TripHistorySinglePage.class);
                intent.putExtra("destination", destination);
                startActivity(intent);
            });
        }

        @NonNull
        @Override
        public CompletedTripAdapter onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.item_completed_trips, parent, false);
            return new CompletedTripAdapter(view);
        }
    };
    recycler_view.setAdapter(adapter);
    adapter.startListening();
}
额外注意事项
  • 如果不想处理泛型擦除的强转逻辑,也可以定义一个无字段的空实体类(仅保留公共无参构造),SDK同样可以正常完成反序列化,比HashMap的类型强转更稳定,不会因为字段类型不匹配直接崩溃。
  • 使用orderByChild("tripCount")排序时,需要在Firebase Realtime Database的规则中为对应路径配置索引,否则会触发权限校验错误导致数据拉取失败。

内容的提问来源于stack exchange,提问作者The greatest

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.25 11:36:04