Android自定义Adapter中TextView的setText无法更新UI问题
嘿,我看了你遇到的问题——在AlertDialog里修改单词和翻译后,TextView的setText只能临时生效,滚动或者编辑其他项就打回原形,重启Activity才会显示正确内容。这问题其实出在Adapter的数据源不同步上,咱们一步步理清楚:
问题根源
你的DictionaryAdapter继承自ArrayAdapter<String>,初始化时传入的是new ArrayList<>(source.keySet())作为默认数据源,但你修改dict的时候,完全没同步更新这个ArrayList!
当你调用notifyDataSetChanged()时,Adapter会重新遍历它自己维护的那个旧的单词列表,在getView里通过getItem(position)拿到旧单词,然后又把holder.word.setText(word)给覆盖了你手动设置的新内容。这就是为什么你手动setText后看起来生效,但一滚动或者其他操作就恢复原状的原因。
另外你代码里还有个小笔误:if(position < visibleText.length)应该是visibleButtons.length,不然会有数组越界风险。
解决方案
核心思路是自己维护Adapter的数据源列表,确保修改dict时同步更新这个列表:
1. 新增自定义数据源列表
在Adapter里添加一个wordList来代替ArrayAdapter默认的数据源:
public class DictionaryAdapter extends ArrayAdapter<String> { private LayoutInflater inflater; private int layout; private Map<String, String> dict; private boolean[] visibleButtons; private Context mContext; private List<String> wordList; // 新增:自己维护单词列表 static class ViewHolder { TextView word; TextView translation; FloatingActionButton edit; } // ... }
2. 修改构造函数,初始化自定义列表
把原来传给super的ArrayList替换成自己的wordList,同时不再依赖ArrayAdapter的默认数据源:
public DictionaryAdapter(Context context, int layout, Map<String, String> source) { super(context, layout); // 这里不再传数据源,我们自己维护 mContext = context; inflater = LayoutInflater.from(context); this.layout = layout; dict = source; wordList = new ArrayList<>(source.keySet()); // 初始化自定义列表 visibleButtons = new boolean[dict.size()]; }
3. 重写getCount和getItem方法
让Adapter使用我们的wordList来提供数据:
@Override public int getCount() { return wordList.size(); } @Override public String getItem(int position) { return wordList.get(position); }
4. 同步更新editWord方法
修改editWord时,不仅要更新dict,还要找到旧单词在wordList中的位置,替换成新单词:
private void editWord(String oldWord, String newWord, String newTranslation) { MainApplication.getDataUtils().editWordInDict(mContext, DictionaryActivity.currentDictionaryName, oldWord, newWord, newTranslation); // 更新字典映射 dict.remove(oldWord); dict.put(newWord, newTranslation); // 同步更新单词列表 int oldIndex = wordList.indexOf(oldWord); if (oldIndex != -1) { wordList.set(oldIndex, newWord); } // 通知Adapter刷新 notifyDataSetChanged(); }
5. 修正笔误
把if(position < visibleText.length)改成if(position < visibleButtons.length),避免数组越界异常。
为什么之前的方法没用?
holder.word.post(Runnable)和自定义changeText:只是临时修改了当前View的内容,但数据源没更新,当ListView复用View或者调用notifyDataSetChanged时,getView又会从旧数据源取数据覆盖掉临时修改。runOnUiThread:其实你的操作本来就在UI线程里(AlertDialog的点击事件是UI线程),所以这个操作完全没用。
现在修改后,你不需要在AlertDialog的点击事件里手动调用holder.word.setText(wordInput)了,因为notifyDataSetChanged()会触发getView重新绘制,自动从更新后的wordList和dict里取新值设置给TextView。当然如果你想即时看到变化,保留手动setText也没问题,现在数据源同步了,不会再被覆盖。
内容的提问来源于stack exchange,提问作者Tom

