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

Android中如何通过FirebaseRecyclerOptions递归获取Firebase数据库Crossfit节点下所有日期的数据

解决方案:递归展示Firebase多层嵌套数据

我来帮你搞定这个问题!你当前的代码无法获取所有数据的核心原因是:FirebaseRecyclerOptions的setQuery方法只会查询指定节点下的直接子项,而你的TrainingTrainerCollector数据藏在「Crossfit -> 日期节点 -> 时间段节点」的两层嵌套之下,直接查询Crossfit节点会把日期节点当成TrainingTrainerCollector对象解析,自然拿不到正确结果。

下面给你两种可行的解决方案,你可以根据需求选择:


方案一:使用嵌套RecyclerView(推荐,适配现有数据库结构)

这个方案通过三层RecyclerView对应数据库的三层结构(日期->时间段->具体数据),逐层加载数据,完美实现递归展示的需求。

步骤1:定义外层日期Adapter

首先创建处理日期节点的Adapter,每个日期项内部包含一个展示时间段的内层RecyclerView:

public class DateAdapter extends FirebaseRecyclerAdapter<DataSnapshot, DateAdapter.DateViewHolder> {
    public DateAdapter(@NonNull FirebaseRecyclerOptions<DataSnapshot> options) {
        super(options);
    }

    @Override
    protected void onBindViewHolder(@NonNull DateViewHolder holder, int position, @NonNull DataSnapshot dateSnapshot) {
        // 显示当前日期(比如APR_09_2021)
        String date = dateSnapshot.getKey();
        holder.dateTv.setText(date);

        // 获取当前日期节点的引用,用于查询下属的时间段节点
        DatabaseReference timeSlotsRef = dateSnapshot.getRef();
        FirebaseRecyclerOptions<DataSnapshot> timeSlotOptions = new FirebaseRecyclerOptions.Builder<DataSnapshot>()
                .setQuery(timeSlotsRef, snapshot -> snapshot) // 直接传递DataSnapshot给下一层
                .build();

        // 初始化时间段Adapter并绑定到内层RecyclerView
        TimeSlotAdapter timeSlotAdapter = new TimeSlotAdapter(timeSlotOptions);
        holder.innerRecyclerView.setLayoutManager(new LinearLayoutManager(holder.itemView.getContext()));
        holder.innerRecyclerView.setAdapter(timeSlotAdapter);
        timeSlotAdapter.startListening();
    }

    @NonNull
    @Override
    public DateViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_date, parent, false);
        return new DateViewHolder(view);
    }

    static class DateViewHolder extends RecyclerView.ViewHolder {
        TextView dateTv;
        RecyclerView innerRecyclerView;

        public DateViewHolder(@NonNull View itemView) {
            super(itemView);
            dateTv = itemView.findViewById(R.id.date_tv);
            innerRecyclerView = itemView.findViewById(R.id.inner_recycler);
        }
    }
}

步骤2:定义时间段Adapter

接着创建处理时间段节点的Adapter,每个时间段项内部包含展示具体数据的最内层RecyclerView:

public class TimeSlotAdapter extends FirebaseRecyclerAdapter<DataSnapshot, TimeSlotAdapter.TimeSlotViewHolder> {
    public TimeSlotAdapter(@NonNull FirebaseRecyclerOptions<DataSnapshot> options) {
        super(options);
    }

    @Override
    protected void onBindViewHolder(@NonNull TimeSlotViewHolder holder, int position, @NonNull DataSnapshot timeSlotSnapshot) {
        // 显示当前时间段(比如10_12)
        String timeSlot = timeSlotSnapshot.getKey();
        holder.timeSlotTv.setText(timeSlot);

        // 获取当前时间段节点的引用,查询下属的具体数据
        DatabaseReference dataRef = timeSlotSnapshot.getRef();
        FirebaseRecyclerOptions<TrainingTrainerCollector> dataOptions = new FirebaseRecyclerOptions.Builder<TrainingTrainerCollector>()
                .setQuery(dataRef, TrainingTrainerCollector.class)
                .build();

        // 使用你已有的TrainerRecyclerviewViewAdapter展示数据
        TrainerRecyclerviewViewAdapter dataAdapter = new TrainerRecyclerviewViewAdapter(dataOptions, "Crossfit");
        holder.dataRecyclerView.setLayoutManager(new LinearLayoutManager(holder.itemView.getContext()));
        holder.dataRecyclerView.setAdapter(dataAdapter);
        dataAdapter.startListening();
    }

    @NonNull
    @Override
    public TimeSlotViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_time_slot, parent, false);
        return new TimeSlotViewHolder(view);
    }

    static class TimeSlotViewHolder extends RecyclerView.ViewHolder {
        TextView timeSlotTv;
        RecyclerView dataRecyclerView;

        public TimeSlotViewHolder(@NonNull View itemView) {
            super(itemView);
            timeSlotTv = itemView.findViewById(R.id.time_slot_tv);
            dataRecyclerView = itemView.findViewById(R.id.data_recycler);
        }
    }
}

步骤3:在Activity中初始化外层RecyclerView

最后替换你原来的代码,初始化外层日期Adapter:

@Override
protected void onStart() {
    super.onStart();
    DatabaseReference crossfitRef = FirebaseDatabase.getInstance().getReference().child("Crossfit");
    
    // 配置日期节点的查询,直接返回DataSnapshot用于下一层处理
    FirebaseRecyclerOptions<DataSnapshot> dateOptions = new FirebaseRecyclerOptions.Builder<DataSnapshot>()
            .setQuery(crossfitRef, snapshot -> snapshot)
            .build();

    DateAdapter dateAdapter = new DateAdapter(dateOptions);
    recyclerView = findViewById(R.id.recycler_plan);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    recyclerView.setAdapter(dateAdapter);
    dateAdapter.startListening();
}

方案二:扁平化数据库结构(适合不想用嵌套RecyclerView的场景)

如果你的业务允许修改数据库结构,可以把所有TrainingTrainerCollector数据扁平化存储,给每条数据新增date和timeSlot字段,然后统一放在一个节点下(比如CrossfitAllData):

CrossfitAllData
  ├─ randomKey1
  │  ├─ date: "APR_09_2021"
  │  ├─ timeSlot: "10_12"
  │  └─ ...(其他原有字段)
  └─ randomKey2
     ├─ date: "APR_10_2021"
     ├─ timeSlot: "14_16"
     └─ ...(其他原有字段)

这样你就能用一行查询获取所有数据,直接复用你原来的Adapter:

DatabaseReference allDataRef = FirebaseDatabase.getInstance().getReference().child("CrossfitAllData");
FirebaseRecyclerOptions<TrainingTrainerCollector> options = new FirebaseRecyclerOptions.Builder<TrainingTrainerCollector>()
        .setQuery(allDataRef, TrainingTrainerCollector.class).build();

recycleViewAdapter = new TrainerRecyclerviewViewAdapter(options,"Crossfit");
recyclerView.setAdapter(recycleViewAdapter);
recycleViewAdapter.startListening();

注意:这种方案需要修改数据写入逻辑,或者用Firebase Cloud Functions自动将嵌套结构的数据同步到扁平化节点。


内容的提问来源于stack exchange,提问作者Andrey Tupikov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 12:37:34