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

