从Firebase实时数据库获取嵌套数据并按科目分组展示问题
问题排查与解决方案
核心问题分析
- 数据模型设计缺陷:原
showDataCustomLayout仅能存储单条测试数据,无法承载「科目-多个测试」的层级结构,不符合需求逻辑。 - Firebase数据读取不完整:仅读取了科目名称,未遍历每个科目下的测试数据,且读取完成后未通知适配器刷新列表。
- 布局与适配器不匹配:原布局和适配器仅适配单条测试数据,无法实现「一个CardView对应一个科目,内部包含多个测试项」的嵌套展示效果。
解决方案步骤
1. 重构数据模型类
创建支持层级结构的模型,每个科目包含对应的测试列表:
public class SubjectMarks { private String subjectName; private List<TestMark> testMarks; public SubjectMarks(String subjectName, List<TestMark> testMarks) { this.subjectName = subjectName; this.testMarks = testMarks; } public String getSubjectName() { return subjectName; } public List<TestMark> getTestMarks() { return testMarks; } // 内部类存储单条测试数据 public static class TestMark { private String testName; private String marks; public TestMark(String testName, String marks) { this.testName = testName; this.marks = marks; } public String getTestName() { return testName; } public String getMarks() { return marks; } } }
2. 修正Firebase数据读取逻辑
完整读取每个科目下的测试数据,并通知适配器更新:
private void showingMarks(String id) { final ArrayList<SubjectMarks> subjectList = new ArrayList<>(); final MarksArrayAdapter adapter = new MarksArrayAdapter(this, subjectList); showSubjectListView.setAdapter(adapter); databaseReference.child("Student Marks/" + id).addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { if (snapshot.exists()) { subjectList.clear(); for (DataSnapshot subjectSnapshot : snapshot.getChildren()) { String subjectName = subjectSnapshot.getKey(); List<SubjectMarks.TestMark> testList = new ArrayList<>(); // 遍历当前科目下的所有测试数据 for (DataSnapshot testSnapshot : subjectSnapshot.getChildren()) { String testName = testSnapshot.getKey(); String marks = testSnapshot.getValue(String.class); testList.add(new SubjectMarks.TestMark(testName, marks)); } subjectList.add(new SubjectMarks(subjectName, testList)); } adapter.notifyDataSetChanged(); // 通知适配器刷新数据 } } @Override public void onCancelled(@NonNull DatabaseError error) { Log.e("FirebaseError", error.getMessage()); } }); }
3. 重写自定义适配器
适配新的层级模型,动态添加测试项到科目Card内:
public class MarksArrayAdapter extends ArrayAdapter<SubjectMarks> { public MarksArrayAdapter(@NonNull Context context, ArrayList<SubjectMarks> subjectList) { super(context, 0, subjectList); } @NonNull @Override public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = LayoutInflater.from(getContext()).inflate(R.layout.subject_card_layout, parent, false); holder = new ViewHolder(); holder.subjectNameTv = convertView.findViewById(R.id.subjectName); holder.testContainer = convertView.findViewById(R.id.test_container); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } SubjectMarks currentSubject = getItem(position); if (currentSubject != null) { holder.subjectNameTv.setText(currentSubject.getSubjectName()); holder.testContainer.removeAllViews(); // 动态添加每个测试项到容器 for (SubjectMarks.TestMark testMark : currentSubject.getTestMarks()) { View testItemView = LayoutInflater.from(getContext()).inflate(R.layout.test_marks_item, holder.testContainer, false); TextView testNameTv = testItemView.findViewById(R.id.test_number); TextView marksTv = testItemView.findViewById(R.id.test_marks); testNameTv.setText(testMark.getTestName()); marksTv.setText(testMark.getMarks()); holder.testContainer.addView(testItemView); } } return convertView; } // ViewHolder优化列表性能 private static class ViewHolder { TextView subjectNameTv; LinearLayout testContainer; } }
4. 调整布局文件
- 科目Card布局(
subject_card_layout.xml):
<?xml version="1.0" encoding="utf-8"?> <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="20dp" app:cardCornerRadius="10dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="10dp"> <TextView android:id="@+id/subjectName" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Subject" android:textColor="#707BFE" android:textSize="20sp" android:textStyle="bold" /> <!-- 动态添加测试项的容器 --> <LinearLayout android:id="@+id/test_container" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" /> </LinearLayout> </androidx.cardview.widget.CardView>
- 测试项布局(
test_marks_item.xml):
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="8dp"> <TextView android:id="@+id/test_number" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="Test 1" android:textAlignment="center" android:textColor="@color/black" android:textSize="16sp" android:textStyle="bold" /> <TextView android:id="@+id/test_marks" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="85" android:textAlignment="center" android:textColor="@color/black" android:textSize="16sp" android:textStyle="bold" /> </LinearLayout>
内容的提问来源于stack exchange,提问作者Dhananjay Singh
相关产品推荐
相关产品推荐

