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

来源:互联网 发布:三天学会mysql 编辑:程序博客网 时间:2024/04/27 23:42

这个问题恐怕很多人在刚开始使用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中存入的数据来判断,而不是复用时的直接使用了,最后附一张完成图。
这里写图片描述

3 0
原创粉丝点击