You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:29:22