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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 16:45:03