RecyclerView条目嵌套双联动Spinner时第二个Spinner全显最后一行数据如何解决
RecyclerView多行Spinner联动时第二个Spinner全部显示最后一行数据的解决方案
问题根源
- 代码中的
scoreList是适配器全局的成员变量,所有行的第二个Spinner的ArrayAdapter都绑定了同一个列表实例。任意行触发Spinner选中事件时调用scoreList.clear()修改内容,所有绑定了该列表的Adapter都会同步刷新,最终所有行的第二个Spinner都会显示最后一次修改(第11行联动触发)的列表数据。 - 编辑场景下仅设置了第一个Spinner的选中值,联动加载完第二个Spinner的列表后没有主动回显之前保存的评分值。
- 单步调试时执行速度慢,每一行的联动逻辑执行完成才会处理下一行的绑定,看起来逻辑正常;正常运行时
setSelection触发的onItemSelected是异步排队执行的,11行的修改操作全部作用于同一个全局列表,最终只剩最后一行的结果。
修复步骤
- 不要使用全局的
scoreList成员变量,每次生成第二个Spinner的数据源时都创建独立的局部ArrayList实例,避免所有行共用同一个数据源。 - 编辑场景下,第二个Spinner的列表加载完成后,主动调用
setSelection回显之前保存的评分数据。 - 注意
funtioningSpinnerList如果是全局成员变量,每次onBindViewHolder执行时都要先清空再添加数据,避免多次绑定后数据重复。
修复后代码示例
// 建议把fortnightLookupList和funtioningSpinnerList的提取逻辑放到适配器初始化方法中,不要每次onBind都执行,提升性能 fortnightLookupList = gCache.getFortNightLookup(); // 如果funtioningSpinnerList是全局变量,每次onBind先清空,避免数据重复 funtioningSpinnerList.clear(); for(int i = 0; i<fortnightLookupList.size(); i++){ funtioningSpinnerList.add(fortnightLookupList.get(i).getFunctioning()); } reasoningAdapter = new ArrayAdapter<>(thisActivity, android.R.layout.simple_spinner_item, funtioningSpinnerList); reasoningAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); holder.functioningSpinner.setAdapter(reasoningAdapter); if (entryStatus.equals(Constants.NEW)) { holder.functioningSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { // 每次创建独立的scoreList,不要用全局变量 List<String> scoreList = new ArrayList<>(); if (pos > 0) { remoteLocationEntryModelData.get(position).checkList = funtioningSpinnerList.get(pos); for (int i = 0; i < fortnightLookupList.size(); i++) { if (fortnightLookupList.get(i).getFunctioning().equals(funtioningSpinnerList.get(pos)) ) { scoreList.add(fortnightLookupList.get(i).getScore()); } } } else { remoteLocationEntryModelData.get(position).checkList = "$"; scoreList.add(0, "---- select ----"); } ArrayAdapter<String> scoreAdapter = new ArrayAdapter<>(thisActivity, android.R.layout.simple_spinner_item, scoreList); scoreAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); holder.scoreSpinner.setAdapter(scoreAdapter); } @Override public void onNothingSelected(AdapterView<?> parent) { } }); } else if (entryStatus.equals(Constants.EDIT)) { if (!TextUtils.isEmpty( String.valueOf( editList.getDescriptions().get(position).getLogId() ) ) ) { remoteLocationEntryModelData.get(position).logId = editList.getDescriptions().get(position).getLogId(); } if (!TextUtils.isEmpty(editList.getDescriptions().get(position).getRemarks())) { holder.remarksEdt.setText(editList.getDescriptions().get(position).getRemarks()); } // 先设置选中监听器,再设置第一个Spinner的选中项,保证联动逻辑正常触发 holder.functioningSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { // 每次创建独立的scoreList,不要用全局变量 List<String> scoreList = new ArrayList<>(); if (pos > 0) { remoteLocationEntryModelData.get(position).checkList = funtioningSpinnerList.get(pos); for (int i = 0; i < fortnightLookupList.size(); i++) { if (fortnightLookupList.get(i).getFunctioning().equals(funtioningSpinnerList.get(pos)) ) { scoreList.add(fortnightLookupList.get(i).getScore()); } } ArrayAdapter<String> scoreAdapter = new ArrayAdapter<>(thisActivity, android.R.layout.simple_spinner_item, scoreList); scoreAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); holder.scoreSpinner.setAdapter(scoreAdapter); // 编辑场景主动回显第二个Spinner的选中值 String savedScore = editList.getDescriptions().get(position).getScore(); int scorePos = scoreList.indexOf(savedScore); if(scorePos >=0){ holder.scoreSpinner.setSelection(scorePos); } } else { remoteLocationEntryModelData.get(position).checkList = "$"; scoreList.add(0, "---- select ----"); ArrayAdapter<String> scoreAdapter = new ArrayAdapter<>(thisActivity, android.R.layout.simple_spinner_item, scoreList); scoreAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); holder.scoreSpinner.setAdapter(scoreAdapter); } } @Override public void onNothingSelected(AdapterView<?> parent) { } }); // 监听器设置完成后再设置第一个Spinner的选中项,触发联动 holder.functioningSpinner.setSelection(funtioningSpinnerList.indexOf(editList.getDescriptions().get(position).getCheckList())); }
额外优化建议
- 把
fortnightLookupList的获取和funtioningSpinnerList的生成逻辑放到适配器的构造方法中执行,不要每次onBindViewHolder都重复遍历,减少不必要的性能消耗。 - 如果列表数据量较大,可以考虑对
fortnightLookupList做分类缓存,比如用HashMap<String, List<String>>保存每个functioning对应的score列表,避免每次联动都遍历整个列表。
内容的提问来源于stack exchange,提问作者Viswa Sundhar
相关产品推荐
相关产品推荐

