解决ListView中滑动复用时控件的混乱问题

这个问题恐怕很多人在刚开始使用ListView时都接触到过,就是比如listview的item中有一个Button或一个CheckBox,你明明点击按键改变了他的背景图片或者问题,又或者勾选了CheckBox,但是你一滑动,发现下面你没操作的item也跟着改变了,然后你再滑动回去,结果原来item上面的操作又变没了。这就是listview中item复用时所产生的问题,下面这种图就是例子。
这里写图片描述

上图就是例子,下滑时,下面明明没有勾选的CheckBox也被勾选上了。当然 这里只是两个例子,群里的小伙伴问过几次,有的item点击字体变色,滑动时没了之类的,这些都同属于一个原理。那么该如何解决这个问题呢?
先分析下这个问题出现的原因:
listview作为一个能加载理论上无限数据的控件,他本是是不存在储存那么多item的空间的,他其实实现了一个复用的机制。先看下面这张图,图是百度的,我自己花了一份 ,太难看了,意思是一样的。
这里写图片描述
上图中一个listview有7个item,但是当他上滑是,item1渐渐消失,item8出来,其实这时候产生的item8出现的的部分就是item1消失的部分,这就是listview的复用,在这种机制下,其实这个listview总共就是对着7个item的操作,却完成了对N多数据的加载。但是这种方法就出现了上述的问题,他会时控件的事件错乱,比如item1中有一个checkbox勾选了,上拉后,item8复后,连勾选这个图像一起复用了,所以导致了listview不断滑动过程中,checkbox的勾选情况不断变化的情况。那么这种情况如何解决呢?
看下面代码:

public class TestAdapter extends BaseAdapter{
    private List<String> list;
    private Context context;
    ViewHolder holder = null;
    public static HashMap<Integer,Boolean> isCheck;
    public static HashMap<Integer,Boolean> isSelect;
    public TestAdapter(List<String> list, Context context) {
        this.list = list;
        this.context = context;
        isCheck = new HashMap<Integer,Boolean>();
        isSelect = new HashMap<Integer,Boolean>();
        initData();
    }

    private void initData() {
        for(int i = 0;i< list.size();i++){
            isCheck.put(i,false);
            setIsCheck(isCheck);
            isSelect.put(i,false);
        }
    }
    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public Object getItem(int position) {
        return list.get(position);
    }

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

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        if(convertView == null){
            holder = new ViewHolder();

            convertView = LayoutInflater.from(context).inflate(R.layout.item,parent,false);
            holder.button = (Button) convertView.findViewById(R.id.button);
            holder.checkBox = (CheckBox) convertView.findViewById(R.id.check_box);
            convertView.setTag(holder);
        }else{
          holder = (ViewHolder) convertView.getTag();
        }
             holder.checkBox.setOnClickListener(new View.OnClickListener() {
                 @Override
                 public void onClick(View v) {
                     if(getIsCheck().get(position)){
                         isCheck.put(position, false);
                         setIsCheck(isCheck);
                     }else{
                         isCheck.put(position, true);
                         setIsCheck(isCheck);
                     }
                 }
             });
        holder.button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {


                if (isSelect.get(position)) {
                    holder.button.setText("按下去");
                    isSelect.put(position, false);
                    notifyDataSetChanged();
                } else {
                    holder.button.setText("弹出来");
                    isSelect.put(position, true);
                    notifyDataSetChanged();

                }
            }
        });

        if(isSelect.get(position)) {
            holder.button.setText("按下去");
        }else{
            holder.button.setText("弹出来");
        }
        if(getIsCheck().get(position)){
                holder.checkBox.setChecked(true);
            }else{
                holder.checkBox.setChecked(false);
            }
        return convertView;
    }
    class ViewHolder{
        Button button;
        CheckBox checkBox;
    }
    public static HashMap<Integer,Boolean> getIsCheck(){
        return TestAdapter.isCheck;
    }
    public static void setIsCheck(HashMap<Integer,Boolean> isCheck){
        TestAdapter.isCheck = isCheck;
    }
}

在这里,我举了两个例子,一个是checkbox,一个是button,当然解决办法也是一样的,这是为了说明其实解决错乱的方法原理是一样的,不管是我写的这俩个,还是item字体颜色的变化之类的。
代码中,我用了HashMap来保存两个数据,第一个就是item的position值,这个值表示的是item真正在listview的序号而不是页面上这个item所在的序号。第二个则是这个position的事件情况,我这里用false表示未勾选,true表示勾选。
第一步:先将listview中所有的checkbox的勾选情况设为false,然后存入HashMap中,然后这段代码

 holder.checkBox.setOnClickListener(new View.OnClickListener() {
                 @Override
                 public void onClick(View v) {
                     if(getIsCheck().get(position)){
                         isCheck.put(position, false);
                         setIsCheck(isCheck);
                     }else{
                         isCheck.put(position, true);
                         setIsCheck(isCheck);
                     }
                 }
             });

当你点击时,改变勾选情况,并将改变的情况在HashMap中更新,这里我写了这两个可供外部调用的方法

  public static HashMap<Integer,Boolean> getIsCheck(){
        return TestAdapter.isCheck;
    }
    public static void setIsCheck(HashMap<Integer,Boolean> isCheck){
        TestAdapter.isCheck = isCheck;
    }

是为外部不如需要全选或不全选时所使用的,你不用也行,那就像我button的那样写。在HashMap存入数据后,在最后一段

if(getIsCheck().get(position)){
                holder.checkBox.setChecked(true);
            }else{
                holder.checkBox.setChecked(false);
            }

中设置checkbox的勾选情况,这样事件就是根据HashMap中存入的数据来判断,而不是复用时的直接使用了,最后附一张完成图。
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值