Android Spinner加载REST API返回的Horario属性并关联ID问题
嘿,我一眼就看出问题所在了——你踩了异步编程里最经典的坑:在异步任务还没完成、数据还没返回的时候,就急着去遍历列表!AsyncTask的execute()方法是异步启动后台任务的,主线程会立刻继续执行后面的代码,这时候你的Horario列表肯定是空的,Spinner自然加载不到数据。
核心解决方案
把Spinner的初始化、数据填充逻辑,全部移到AsyncTask的onPostExecute()方法里。这个方法是后台任务完成后自动在主线程调用的,刚好适合更新UI,而且此时你已经拿到了完整的Horario列表。
修改后的代码示例
我帮你调整了FaltasActivity里的HorarioATask,重点优化了数据加载后的UI处理逻辑,同时帮你做好了选中时间后存储对应ID的准备:
public class FaltasActivity extends AppCompatActivity { private Spinner horarioSpinner; private int selectedHorarioId; // 用于存储选中的日程ID,提交缺勤请求用 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_faltas); horarioSpinner = findViewById(R.id.your_spinner_id); // 替换成你的Spinner ID // 启动异步任务获取日程数据 new HorarioATask().execute(); } private class HorarioATask extends AsyncTask<Void, Void, List<Horario>> { @Override protected List<Horario> doInBackground(Void... voids) { // 这里调用你的自研REST API获取Horario列表 // 注意:处理好网络异常、解析异常,避免返回null try { return YourApiClient.getHorarios(selectedCourseId, selectedStudentId); // 替换成你的API调用逻辑,传入课程和学生参数 } catch (Exception e) { e.printStackTrace(); return null; } } @Override protected void onPostExecute(List<Horario> horarioList) { super.onPostExecute(horarioList); // 先判断数据是否有效 if (horarioList == null || horarioList.isEmpty()) { Toast.makeText(FaltasActivity.this, "未获取到可用日程", Toast.LENGTH_SHORT).show(); return; } // 准备Spinner显示的时间列表,以及时间和ID的映射关系 List<String> inicioTimeList = new ArrayList<>(); Map<String, Integer> timeToIdMap = new HashMap<>(); for (Horario horario : horarioList) { String inicioTime = horario.getInicio(); inicioTimeList.add(inicioTime); timeToIdMap.put(inicioTime, horario.getId()); // 绑定时间和对应ID } // 设置Spinner适配器 ArrayAdapter<String> adapter = new ArrayAdapter<>( FaltasActivity.this, android.R.layout.simple_spinner_item, inicioTimeList ); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); horarioSpinner.setAdapter(adapter); // 设置选中监听,获取对应ID horarioSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { String selectedTime = parent.getItemAtPosition(position).toString(); selectedHorarioId = timeToIdMap.get(selectedTime); // 这里可以测试输出,确认ID是否正确 Log.d("FaltasActivity", "选中的日程ID:" + selectedHorarioId); } @Override public void onNothingSelected(AdapterView<?> parent) { // 可选:处理未选中的情况,比如默认设置第一个ID if (!horarioList.isEmpty()) { selectedHorarioId = horarioList.get(0).getId(); } } }); } } }
关键注意点
参数传递:如果你的API需要课程和学生的ID作为参数,可以在
HorarioATask的构造方法里传入,比如:private class HorarioATask extends AsyncTask<Void, Void, List<Horario>> { private int courseId; private int studentId; public HorarioATask(int courseId, int studentId) { this.courseId = courseId; this.studentId = studentId; } // 在doInBackground里用这两个参数调用API }调用时就传参数:
new HorarioATask(selectedCourseId, selectedStudentId).execute();异常处理:在
doInBackground里一定要捕获网络请求、JSON解析的异常,避免因为请求失败导致onPostExecute拿到null而崩溃。替代方案(可选):如果是新项目,官方已经不推荐使用AsyncTask了,建议改用Kotlin协程或者Retrofit+LiveData,这些方案的异步逻辑更清晰,也更稳定。不过既然你已经用了AsyncTask,先解决当前问题是优先的。
内容的提问来源于stack exchange,提问作者Víctor Vanaclocha Cebrián

