ListView自定义Adapter:点击列表项按钮更新TextView后滚动恢复的问题
问题分析
这个问题我太熟悉了——这完全是ListView的视图复用特性在搞鬼!你点击按钮时只是临时修改了当前View上的控件状态,但没把这些状态同步保存到数据源(Bookdata)里。当列表滚动时,系统会回收并复用之前的View(也就是convertView),这时候getView方法会重新从数据源读取原始数据刷新UI,之前修改的状态自然就丢失了,TextView变回原样、Button重新显示也就不奇怪了。
解决方案
核心思路很简单:把UI状态和数据源绑定,让每个列表项的状态都存在对应的Bookdata对象里,这样每次复用View时,都能从数据源读取正确状态渲染UI。
步骤1:修改数据源类Bookdata
给Bookdata加两个字段,分别记录按钮是否被点击、TextView的最新内容:
public class Bookdata { private String name; // 标记按钮是否已被点击 private boolean isClicked; // 保存TextView要显示的最新文本 private String displayText; public Bookdata() { // 默认未点击,显示原始name this.isClicked = false; } public String getName() { return name; } public void setName(String name) { this.name = name; // 初始时默认显示name this.displayText = name; } public boolean isClicked() { return isClicked; } public void setClicked(boolean clicked) { isClicked = clicked; } public String getDisplayText() { return displayText; } public void setDisplayText(String displayText) { this.displayText = displayText; } }
步骤2:修改CustomAdapter的getView方法
在getView里根据Bookdata的状态渲染UI,点击按钮时同步更新数据源并通知适配器刷新:
public class CustomAdapter extends BaseAdapter { ArrayList<Bookdata> cusList = new ArrayList<>(); LayoutInflater inflater; Context context; public CustomAdapter(Context context, ArrayList<Bookdata> cusList) { this.cusList = cusList; this.context = context; inflater = LayoutInflater.from(this.context); } @Override public int getCount() { return cusList.size(); } @Override public Bookdata getItem(int position) { return cusList.get(position); } @Override public long getItemId(int position) { return position; // 返回position更合理,避免复用冲突 } @Override public View getView(int position, View convertView, ViewGroup parent) { final ViewHolder viewHolder; if (convertView == null) { convertView = inflater.inflate(R.layout.list, parent, false); viewHolder = new ViewHolder(convertView); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } final Bookdata data = getItem(position); // 从数据源读取状态渲染UI viewHolder.mytv.setText(data.getDisplayText()); viewHolder.mybtn.setVisibility(data.isClicked() ? View.GONE : View.VISIBLE); viewHolder.mybtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 更新数据源状态 data.setClicked(true); data.setDisplayText("Clicked"); // 通知适配器刷新,局部刷新用notifyItemChanged(position)更高效 notifyDataSetChanged(); } }); return convertView; } private class ViewHolder { TextView mytv; Button mybtn; public ViewHolder(View item) { mytv = item.findViewById(R.id.mytv); mybtn = item.findViewById(R.id.mybtn); } } }
步骤3:初始化代码优化(可选)
给列表初始化代码加上泛型,避免类型转换警告:
ListView books= findViewById(R.id.books); ArrayList<Bookdata> bookList = new ArrayList<>(); for (int i = 0; i < 10; i++) { Bookdata data = new Bookdata(); data.setName("position "+i); bookList.add(data); } CustomAdapter allbookAda= new CustomAdapter(this, bookList); books.setAdapter(allbookAda); // 初始化时不需要调用notifyDataSetChanged(),setAdapter已经会触发刷新
这样修改后,每次点击按钮的状态都会存在数据源里,ListView滚动复用View时,会自动读取最新状态渲染UI,再也不会出现状态丢失的问题啦!
内容的提问来源于stack exchange,提问作者user8493027
相关产品推荐
相关产品推荐

