convertView 復用機制:getView(position, convertView, parent) 中,convertView 是被回收的舊 View。若**不根據數據模型重置所有子控件狀態**,舊狀態會「幽靈」般出現在新位置。
public class MyAdapter extends BaseAdapter {
private List data; // ItemData { int imgRes; String name; boolean checked; }
static class ViewHolder {
ImageView imgContent;
TextView tvName;
CheckBox cbCheck;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.grid_item, parent, false);
holder = new ViewHolder();
holder.imgContent = convertView.findViewById(R.id.img_content);
holder.tvName = convertView.findViewById(R.id.tv_name);
holder.cbCheck = convertView.findViewById(R.id.cb_check);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
// ★ 關鍵:每次必須根據數據模型**完全重置**所有可變狀態
ItemData item = data.get(position);
holder.imgContent.setImageResource(item.imgRes);
holder.tvName.setText(item.name);
holder.cbCheck.setChecked(item.checked); // 強制同步
// ★ 點擊事件修改數據模型,不直接改 View
holder.cbCheck.setOnClickListener(v -> {
item.checked = !item.checked;
notifyDataSetChanged(); // 或 notifyItemChanged(position) 若用 RecyclerView
});
return convertView;
}
}
else setChecked(false) 或直接 setChecked(item.checked)。setTag(position) 並在 Listener 里取 tag,或統一在 Activity 持有 OnItemClickListener。notifyItemChanged 粒度更細,自帶動畫,性能更好。**Adapter 只負責「數據 → View」的單向映射**。任何可交互狀態(勾選、輸入、展開)必須**持久化到數據模型**,getView 中**無條件同步**。這是 ListView/GridView/RecyclerView 正確使用的鐵律。