关于Android的ListView中CheckBox错乱

本文参考: > ListView中getView的原理+如何在ListView中放置多个item

上文中关于说的Android中的Recycler比较模糊,Android会维持屏幕中显示的item+1个对象。但是具体的复用机制没有介绍,建议阅读源码学习。

关于checkBox,选中了之后,下滑之后返回就消失的问题。

有以下两种方式供大家参考:

data是给TextView显示的数据

isChecked是用来记录CheckBox是否选中的。

ArrayList<String> data;
ArrayList<Boolean> isChecked;
 class Holder {
        TextView name;
        CheckBox box;
    }

  

方法一:

用onClickedListener,使用这个监听器可以简单的避开CheckedBox选中消失的问题。

其中:

holder.box.setTag(i);

用来给监听器指明选择的索引。

 @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        if(view == null) {
            view = LayoutInflater.from(context).inflate(R.layout.list_item, null);
            holder = new Holder();
            holder.box = (CheckBox) view.findViewById(R.id.cb_isChecked);
            holder.name = (TextView) view.findViewById(R.id.tv_name);
            view.setTag(holder);
        } else {
            holder = (Holder) view.getTag();
        }
        holder.name.setText(data.get(i));
        System.out.println("即将改变成" + isChecked.get(i) + "i" + i);
        holder.box.setChecked(isChecked.get(i));
  
        holder.box.setTag(i);
        holder.box.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                boolean hold = isChecked.get((Integer) view.getTag());
                isChecked.set((Integer) view.getTag(), !hold);
                System.out.println("compoundButton.getTag()" + view.getTag());
            }
        });
        return view;
    }

方法二:

继续使用onCheckedListener这个监听器,需要注意一些问题。

代码:和上面的一样,就是把onClickListener改成onCheckedlistener,结果就出现了选中消失的问题。

仔细看了在logcat中看了输出的结果,发现问题的所在:

当我们选中一个item时,假设此时item1被选中,下拉列表,到时item1隐藏,item10出现,并复用了item1的结构。

由于在getView函数中item10被赋值是false,此时,onCheckedListener监听器响应了(如果使用onClickedListener就没有以下问题,因为它再此时不会响应)。

问题来了:

因为在onCheckedListener根据view.getTag()来获得索引,而此时的索引是1,就是item1对象时setTag时留下的。

最终导致此时监听器把用来存储布尔值的线性表的索引1的位置赋值成了item10的值。

根据问题的成因,我们需要在 onCheckedListener响应前,让view.getTag()获得正确的索引。

其实很简单只需要将

holder.box.setTag(i);

放到

holder.box.setChecked(isChecked.get(i));

前面就可以了。 此解决方案的完整代码:

public class ListAdapter2 extends BaseAdapter {
    ArrayList<String> data;
    ArrayList<Boolean> isChecked;
    Context context;
    Holder holder;
    public ListAdapter2(Context context, ArrayList<String> data, ArrayList<Boolean> isChecked) {
        this.context = context;
        this.data = data;
        this.isChecked = isChecked;
    }

@Override
public int getCount() {
    return data.size();
}

@Override
public Object getItem(int i) {
    return data.get(i);
}

@Override
public long getItemId(int i) {
    return i;
}

@Override
public View getView(int i, View view, ViewGroup viewGroup) {
    if(view == null) {
        view = LayoutInflater.from(context).inflate(R.layout.list_item, null);
        holder = new Holder();
        holder.box = (CheckBox) view.findViewById(R.id.cb_isChecked);
        holder.name = (TextView) view.findViewById(R.id.tv_name);
        view.setTag(holder);

    } else {
        holder = (Holder) view.getTag();
    }

    holder.name.setText(data.get(i));
    System.out.println("即将改变成" + isChecked.get(i) + "i" + i);
    holder.box.setTag(i);

    holder.box.setChecked(isChecked.get(i));

    holder.box.setOnCheckedChangeListener(new CheckedListener());
    return view;
}

class CheckedListener implements CompoundButton.OnCheckedChangeListener {
    @Override
    public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
        isChecked.set((Integer) compoundButton.getTag(), b);
    }
}

class Holder {
    TextView name;
    CheckBox box;
}
}