Android(Java)基于Volley接口返回的动态JSON构建自适应UI的实现问题
动态列适配改造方案
1. 重构数据模型
删除原模型中写死的Test1~Test6分数字段,改为动态集合存储,同时新增全局变量存储动态表头:
// 全局变量 List<String> dynamicColumnHeaders = new ArrayList<>(); // 存储除序号、姓名外的动态列名 List<GrandTestMarksModel> grandTestMarksModels = new ArrayList<>(); // 改造后的GrandTestMarksModel public class GrandTestMarksModel { private String serialNumber; private String studentName; private List<String> marksList; // 动态存储所有分数列 public GrandTestMarksModel(String serialNumber, String studentName, List<String> marksList) { this.serialNumber = serialNumber; this.studentName = studentName; this.marksList = marksList; } // 自行添加对应getter方法 }
2. 修改Volley JSON解析逻辑
不再硬编码读取固定字段,先从第一条返回数据中提取动态列名,再批量解析所有学生数据:
response -> { try { // 先清空旧数据 grandTestMarksModels.clear(); dynamicColumnHeaders.clear(); if (response.length() == 0) { viewGrandTestMarksAdapter.notifyDataSetChanged(); return; } // 第一步:提取动态列名(所有非固定字段都是分数列) JSONObject firstItem = response.getJSONObject(0); Iterator<String> keys = firstItem.keys(); while (keys.hasNext()) { String key = keys.next(); // 过滤固定字段,剩下的就是动态分数列的字段名 if (!key.equals("StudentName")) { dynamicColumnHeaders.add(key); } } // 可选:对列名排序,保证Test1、Test2...的顺序正确 Collections.sort(dynamicColumnHeaders, (o1, o2) -> { int num1 = Integer.parseInt(o1.replaceAll("[^0-9]", "")); int num2 = Integer.parseInt(o2.replaceAll("[^0-9]", "")); return num1 - num2; }); // 第二步:解析所有学生数据 for (int i = 0; i < response.length(); i++) { JSONObject jsonObject = response.getJSONObject(i); List<String> marks = new ArrayList<>(); // 按动态列名读取对应分数 for (String column : dynamicColumnHeaders) { marks.add(jsonObject.getString(column)); } GrandTestMarksModel model = new GrandTestMarksModel( String.valueOf(i + 1), jsonObject.getString("StudentName"), marks ); grandTestMarksModels.add(model); } // 第三步:更新适配器(原代码放在for循环内会重复执行,必须移到循环外) recyclerView.setAdapter(viewGrandTestMarksAdapter); // 若适配器已提前初始化,直接调用notifyDataSetChanged即可 // viewGrandTestMarksAdapter.notifyDataSetChanged(); } catch (JSONException e) { e.printStackTrace(); } }
3. 改造RecyclerView相关实现
3.1 修改列表项布局
删除布局中写死的分数TextView,新增水平线性布局作为动态列的容器:
<!-- item_grand_test_mark.xml --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/tv_serial" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:padding="8dp"/> <TextView android:id="@+id/tv_student_name" android:layout_width="0dp" android:layout_weight="2" android:layout_height="wrap_content" android:padding="8dp"/> <!-- 动态分数列容器 --> <LinearLayout android:id="@+id/ll_marks_container" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal"/> </LinearLayout>
可选:提前创建分数项的子布局item_mark_text.xml,统一样式:
<!-- item_mark_text.xml --> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="80dp" android:layout_height="wrap_content" android:padding="8dp" android:gravity="center"/>
3.2 改造RecyclerView适配器
在绑定数据时动态生成对应数量的分数列:
public class GrandTestMarksAdapter extends RecyclerView.Adapter<GrandTestMarksAdapter.ViewHolder> { private Context context; private List<GrandTestMarksModel> dataList; public GrandTestMarksAdapter(Context context, List<GrandTestMarksModel> dataList) { this.context = context; this.dataList = dataList; } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(context).inflate(R.layout.item_grand_test_mark, parent, false); return new ViewHolder(view); } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { GrandTestMarksModel model = dataList.get(position); holder.tvSerial.setText(model.getSerialNumber()); holder.tvStudentName.setText(model.getStudentName()); // 先清空容器内旧的View,避免列表复用混乱 holder.llMarksContainer.removeAllViews(); // 动态添加分数列 for (String mark : model.getMarksList()) { TextView tvMark = (TextView) LayoutInflater.from(context).inflate(R.layout.item_mark_text, holder.llMarksContainer, false); tvMark.setText(mark); holder.llMarksContainer.addView(tvMark); } } @Override public int getItemCount() { return dataList.size(); } public static class ViewHolder extends RecyclerView.ViewHolder { TextView tvSerial, tvStudentName; LinearLayout llMarksContainer; public ViewHolder(@NonNull View itemView) { super(itemView); tvSerial = itemView.findViewById(R.id.tv_serial); tvStudentName = itemView.findViewById(R.id.tv_student_name); llMarksContainer = itemView.findViewById(R.id.ll_marks_container); } } }
4. 实现动态表头
在页面顶部添加和列表项结构一致的表头布局,拿到dynamicColumnHeaders后,用和适配器相同的逻辑,动态往表头的容器中添加对应列名的TextView即可。
内容的提问来源于stack exchange,提问作者Rizwan Abbasi
相关产品推荐
相关产品推荐

