在listView适配器的内存优化给点击事件带来的麻烦

来源:互联网 发布:淘宝闺蜜投诉平台 编辑:程序博客网 时间:2024/05/29 18:16

最近做了一个listview,想在点击Item的时候让这个Item的图标改变,监听器在适配器里面做,结果跑起来发现,点击之后,改变图标的竟然不是所点击的Item,而是屏幕中最后一个Item(注意!不是最后一个Item,而是屏幕中已经显示的Item的最后一个)
先贴一段适配器代码

public class DetailAdapter extends BaseAdapter {    private List<FoodDetail> foodDetail;    private LayoutInflater layoutInflater;//加载布局的准备工作    private Context context;    private int local;    private int [] image;    ViewHolder holder;    public DetailAdapter(List<FoodDetail> foodDetail,Context context,int[] image) {        this.foodDetail = foodDetail;        this.context=context;        layoutInflater = LayoutInflater.from(context);        this.image = image;    }    @Override    public int getCount() {        // TODO Auto-generated method stub        return foodDetail.size();    }    @Override    public Object getItem(int position) {        // TODO Auto-generated method stub        return foodDetail.get(position);    }    @Override    public long getItemId(int position) {        // TODO Auto-generated method stub        return position;    }    @Override    public View getView(int position, View convertView, ViewGroup parent) {        // TODO Auto-generated method stub        local = position;        if (convertView == null) {            holder = new ViewHolder();            convertView = layoutInflater.inflate(R.layout.listview_detail, null);            holder.relativeLayout = (RelativeLayout) convertView.findViewById(R.id.one);            holder.imageView = (ImageView)             convertView.setTag(holder);        } else {            holder = (ViewHolder) convertView.getTag();        }        holder.imageView.setBackgroundResource(image[position]);holder.imageView.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                //注意设置imageview背景这段话                holder.imageView.setBackground(background);            }        });        return convertView;    }    public static class ViewHolder {        public ImageView imageView;    }}

如上面代码,在onClick()方法里修改Imageview的背景,实际上所修改的不是你当前所点击的Item,而是屏幕中所显示的最后一条Item的Imageview!
注意!!!!!不是最后一个Item,而是屏幕中所显示的最后一个Item。这一切都是listview在内存优化方面做的太好的“错”!
下面顺便说下lisview的内存机制
先看一张图片:这里写图片描述
注意里面test数值的变化(不要看a),test变量是我在getView()方法里定义的一个final的int常量,final int test = (int) ( 50 * Math.random() + 50);
值为50-100的随机数,点击listview的任意一个Item,就会打印log日志!通过我的多次实验,发现如果不滑动listview,只是一直点击某一个Item的时候,test的数值是不会改变的!然而,当你滑动listview之后,再次点击这个Item,就会发现test的值已经改变!
这也验证了listview的加载过程不是一下子全部加载,而是至加载一部分,滑动之前的test在滑动的时候,会被内存多次收回和重新建立,当再次回到你滑动之前的Item的时候,这个常量已经不再是当年的那个常量了;
上个图:
这里写图片描述
下面解释一下:
1、如果你有几千几万甚至更多的选项(item)时,其中只有可见的项目(满屏显示的Item数目)存在内存(说的优化就是说在内存中的优化!)中,其他的在Recycler中

2、ListView先请求一个type1视图(getView)然后请求其他可见的项目。convertView在getView中是空(null)的,第一次都是为空的,只要显示过了convertView都不为空,会保存在Recycler中

3、当item1滚出屏幕,并且一个新的项目从屏幕低端上来时,ListView再请求一个type1视图。convertView此时不是空值了,它的值是item1。你只需设定新的数据然后返回convertView,不必重新创建一个视图,省去了inflate和findViewById的时间,性能就得到了优化。
接着说上面的问题:当你older.imageView.setBackground(background);的时候,实际上,这个控件已经不是你点击的那个Item的了。而是指向屏幕中显示的最后一个Item。所以才造成了无论我点击屏幕中的任何Item,都只是最后一个Item在改变

解决的方法就是使用监听事件里的参数去获取你想要改变的布局,就是那个View v参数,

public void onClick(View v) {                holder.imageView.setBackground(background);            }

使用这个v.getparent()一层层获取到你想要的东西,最后要记住强制转换,因为你获取到的是view控件,也许没有你想要的方法。
或者,简单粗暴:

holder.ImageButton.setOnClickListener(new OnClickListener() {                @Override                public void onClick(View v) {                    //通过v获取当前你的所点击的Item                    ImageButton imageButton = (ImageButton) v;                    imageButton.setBackgroundResource(R.drawable.started);                    }             });

这样,天下太平,你点击哪个Item,哪个Item的图标就改变!!
最后,希望大家多多指教,欢迎补充更好的方法!

1 1
原创粉丝点击