如何对比RecyclerView选项与Firebase Realtime Database存储的正确答案
问答应用RecyclerView批量批改实现方案
你正在开发的问答应用数据库结构如下:
现有单题批改逻辑运行正常,要实现所有题目批量批改,核心需要解决两个问题:
- 单题批改方法中使用全局的
rgrex、resp1/resp2/resp3控件,只能对应单个题目,无法匹配RecyclerView每个独立条目里的控件 - 每次批改单题都单独请求Firebase,效率低,也不适合批量处理场景
实现步骤
步骤1:改造RecyclerView.Adapter,存储用户选择和题目数据
在Adapter里存储所有题目列表,以及每个题目的用户选择,避免RecyclerView复用时选择混乱,同时对外暴露数据获取接口:
public class PreguntaAdapter extends RecyclerView.Adapter<PreguntaAdapter.PreguntaHolder> { // 所有题目数据,初始化时一次性从Firebase拉取 private List<PreguntaExamen> preguntaList; // 存储用户选择:key为条目position,value为选中的选项序号1/2/3,0代表未选择 private SparseIntArray userSelections = new SparseIntArray(); // 是否处于已批改状态,用来处理屏幕外条目UI更新 private boolean isCorrected = false; public PreguntaAdapter(List<PreguntaExamen> list) { this.preguntaList = list; } public SparseIntArray getUserSelections() { return userSelections; } public List<PreguntaExamen> getPreguntaList() { return preguntaList; } public void setCorrected(boolean corrected) { isCorrected = corrected; notifyDataSetChanged(); } @NonNull @Override public PreguntaHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_pregunta, parent, false); return new PreguntaHolder(v); } @Override public void onBindViewHolder(@NonNull PreguntaHolder holder, int position) { PreguntaExamen pregunta = preguntaList.get(position); holder.resp1.setText(pregunta.getOpcion1()); holder.resp2.setText(pregunta.getOpcion2()); holder.resp3.setText(pregunta.getOpcion3()); // 重置监听器避免复用冲突 holder.rgPregunta.setOnCheckedChangeListener(null); holder.rgPregunta.clearCheck(); // 回显已选选项 int select = userSelections.get(position, 0); if (select != 0) { switch (select) { case 1: holder.rb1.setChecked(true); break; case 2: holder.rb2.setChecked(true); break; case 3: holder.rb3.setChecked(true); break; } } // 已批改状态下更新UI、禁用选择 if (isCorrected) { int correct = Integer.parseInt(pregunta.getCorrecta()); holder.resp1.setTextColor(Color.RED); holder.resp2.setTextColor(Color.RED); holder.resp3.setTextColor(Color.RED); switch (correct) { case 1: holder.resp1.setTextColor(Color.GREEN); break; case 2: holder.resp2.setTextColor(Color.GREEN); break; case 3: holder.resp3.setTextColor(Color.GREEN); break; } holder.rgPregunta.setEnabled(false); for (int i = 0; i < holder.rgPregunta.getChildCount(); i++) { holder.rgPregunta.getChildAt(i).setEnabled(false); } return; } // 监听用户选择 holder.rgPregunta.setEnabled(true); holder.rgPregunta.setOnCheckedChangeListener((group, checkedId) -> { int s = 0; if (checkedId == R.id.rb1) s = 1; else if (checkedId == R.id.rb2) s = 2; else if (checkedId == R.id.rb3) s = 3; userSelections.put(position, s); }); } @Override public int getItemCount() { return preguntaList.size(); } public static class PreguntaHolder extends RecyclerView.ViewHolder { RadioGroup rgPregunta; RadioButton rb1, rb2, rb3; TextView resp1, resp2, resp3; public PreguntaHolder(@NonNull View itemView) { super(itemView); rgPregunta = itemView.findViewById(R.id.rg_pregunta); rb1 = itemView.findViewById(R.id.rb1); rb2 = itemView.findViewById(R.id.rb2); rb3 = itemView.findViewById(R.id.rb3); resp1 = itemView.findViewById(R.id.tv_resp1); resp2 = itemView.findViewById(R.id.tv_resp2); resp3 = itemView.findViewById(R.id.tv_resp3); } } }
步骤2:页面初始化时一次性拉取所有题目数据
不需要每次批改再单独请求Firebase,初始化列表时就把所有题目(包含正确答案)拉取到本地:
// 页面onCreate方法中添加 database.child("Enfermeria").addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { List<PreguntaExamen> preguntaList = new ArrayList<>(); for (DataSnapshot ds : snapshot.getChildren()) { PreguntaExamen p = ds.getValue(PreguntaExamen.class); if (p != null) { preguntaList.add(p); } } myAdapter = new PreguntaAdapter(preguntaList); recyclerView.setAdapter(myAdapter); } @Override public void onCancelled(@NonNull DatabaseError error) {} });
步骤3:修改批改按钮点击逻辑,批量处理所有题目
直接遍历本地存储的题目和用户选择,一次性完成所有题目的批改:
btnacab.setOnClickListener(v -> { pauseTimer(); if (!corregido) { int totalScore = 0; SparseIntArray selections = myAdapter.getUserSelections(); List<PreguntaExamen> preguntaList = myAdapter.getPreguntaList(); // 遍历所有题目计算得分 for (int i = 0; i < preguntaList.size(); i++) { PreguntaExamen p = preguntaList.get(i); int correct = Integer.parseInt(p.getCorrecta()); int userSelect = selections.get(i, 0); if (userSelect == correct) { totalScore++; } } // 更新得分 tvpuntu.setText("Puntuación: " + totalScore + "/" + preguntaList.size()); // 标记为已批改,刷新RecyclerView更新所有条目UI myAdapter.setCorrected(true); corregido = true; btnacab.setText("SALIR"); } else { Intent i = new Intent(ModeloExamen3.this, GeneralTest.class); startActivity(i); } });
注意事项
- 该方案一次性拉取所有题目到本地,减少了Firebase请求次数,响应速度更快
- 批改状态放在Adapter中处理,即便是超出屏幕的条目,滚动时也会自动更新选项颜色,不会出现UI遗漏的问题
- 所有用户选择都存储在Adapter中,避免了RecyclerView条目复用导致的选择错乱问题
内容的提问来源于stack exchange,提问作者Adrian S.
相关产品推荐
相关产品推荐

