标签:
在我的上一篇博客《Android ListView基础篇》中陈列了ListView和adapter的多种结合方式的基本使用,在本篇文章中将具体讲述如何通过多种方式处理好ListView的优化问题。我们通过上一篇的例子做个测试,将行数调到100,在getView中打印一句Log看看:
Log打印结果:
可以看到,初始化ListView时getView运行了9次,而界面上刚好也仅显示到第9条数据,也就是只有屏幕范围内显示的才会调用getView(),另外,可以看到它们的convertView都会null,然后我们再将界面稍微往下拖动,如图:
再看Logcat:
注意到,第九项数据从底部开始进入界面,它的getView也调用了一遍,convertView依然为null,这是因为顶部的第一项数据还未完全脱离屏幕范围外,也就是第一项的视图还未进入Android的Recycler中,还不能被重用,我们再继续往下滑:
Logcat:
发现第10项的convertView不为空了!这是因为顶部的第一项数据已经完全离开了屏幕,所以Android会将它的convertView“推”进RecycleView中,然后第10行出现的时候,getView方法的convertView参数正是第一项存放在Recycler中的视图。如下图:
@Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub Log.d("getView--->", convertView+"--position:"+position); if(convertView==null){ convertView = inflater.inflate(R.layout.list_item, null); } TextView text = (TextView)convertView.findViewById(R.id.list_item_text); ImageView image = (ImageView)convertView.findViewById(R.id.list_item_image); text.setText(data.get(position).get("text").toString()); image.setImageResource(Integer.parseInt(data.get(position).get("image").toString())); return convertView; }
运行滑动到如下图:
打印结果:
注意我圈起来的两个地方,两个地址一模一样!所以我们成功重用了Recycle中缓存的视图,这样可以有效优化ListView的内存消耗(试想一下,100000个视图我来来回回只用那10个convertView,能不减少内存开销吗?)
@Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub Log.d("getView--->", convertView+"--position:"+position); ViewHolder holder = null; if(convertView==null){ convertView = inflater.inflate(R.layout.list_item, null); holder = new ViewHolder(); holder.text = (TextView)convertView.findViewById(R.id.list_item_text); holder.image = (ImageView)convertView.findViewById(R.id.list_item_image); convertView.setTag(holder); } else{ holder = (ViewHolder)convertView.getTag(); } holder.text.setText(data.get(position).get("text").toString()); holder.image.setImageResource(Integer.parseInt(data.get(position).get("image").toString())); return convertView; } public static class ViewHolder{ public TextView text; public ImageView image; }
代码分析:首先自定义了一个ViewHolder类,这里设置为static,这样ViewHolder无论new多少次都是指向同一个内存空间,在ViewHolder类中添加了两个成员变量,分别对应我们的子控件。每次getView的时候,同样先判断ViewHolder对象是否为空,如果为空,就实例化一个ViewHolder对象,并将convertView通过findViewById找到的子控件赋给holder,再将holder通过setTag()方法设置在convertView上,之后重用的时候可以通过convertView的getTag()来获得。其实ViewHolder相当于我们子控件的一个封装类而已,通过这样实现不用每次都去findViewById查找子控件,每次做的事情只是重用之前的视图和控件设置一下数据,达到优化的目的。
代码如下:
public class ListViewAdapter extends SimpleAdapter{ private Context context; private List<Map<String,Object>> data; private LayoutInflater inflater; //注意,这里定义的这些整型数要小于getViewTypeCount()所返回的那个数字,否则会报错越界 private final int TYPE_1 = 0; private final int TYPE_2 = 1; public ListViewAdapter(Context context, List<Map<String, Object>> data, int resource, String[] from, int[] to) { super(context, data, resource, from, to); // TODO Auto-generated constructor stub this.context = context; this.data = data; inflater = LayoutInflater.from(context); } //返回数据的大小,即listview的行数 @Override public int getCount() { // TODO Auto-generated method stub return data.size(); } //根据下标获得某一行的数据 @Override public Object getItem(int position) { // TODO Auto-generated method stub return data.get(position); } //获得指定的Item的下标 @Override public long getItemId(int position) { // TODO Auto-generated method stub return position; } @Override public int getItemViewType(int position) { // TODO Auto-generated method stub //如果当前行是偶数行,返回类型1 if(position%2==0){ return TYPE_1; } //如果当前行是奇数行,返回类型2 else{ return TYPE_2; } } @Override public int getViewTypeCount() { // TODO Auto-generated method stub return 2; } @Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub Log.d("getView--->", convertView+"--position:"+position); ViewHolder1 holder1 = null; ViewHolder2 holder2 = null; int type = getItemViewType(position); if(convertView==null){ switch (type) { case TYPE_1: convertView = inflater.inflate(R.layout.list_item, null); holder1 = new ViewHolder1(); holder1.text = (TextView)convertView.findViewById(R.id.list_item_text); holder1.image = (ImageView)convertView.findViewById(R.id.list_item_image); convertView.setTag(holder1); break; case TYPE_2: convertView = inflater.inflate(R.layout.list_item2, null); holder2 = new ViewHolder2(); holder2.text = (TextView)convertView.findViewById(R.id.list_item_text2); holder2.detail = (TextView)convertView.findViewById(R.id.list_item_detail2); convertView.setTag(holder2); break; } } else{ switch (type) { case TYPE_1: holder1 = (ViewHolder1)convertView.getTag(); break; case TYPE_2: holder2 = (ViewHolder2)convertView.getTag(); break; } } switch (type) { case TYPE_1: holder1.text.setText(data.get(position).get("text").toString()); holder1.image.setImageResource(Integer.parseInt(data.get(position).get("image").toString())); break; case TYPE_2: holder2.text.setText(data.get(position).get("text").toString()); holder2.detail.setText(data.get(position).get("text").toString()); break; } return convertView; } public static class ViewHolder1{ public TextView text; public ImageView image; } public static class ViewHolder2{ public TextView text; public TextView detail; } }
代码分析:创建另外一个ViewHolder,用于加载和重用另外一种布局,其实就是在原来的基础上,为每个操作都套上一层switch判断,然后根据type的类型来分别设置两种布局。
总之,以上讲述了ListView的多种优化方式,但是并不是万能,也仅仅只是起到了一部分效果,真实开发中还要视情况而定,比如如果是多图片,首先需要将图片压缩,并且不要再getView中做过多的耗时操作!希望本文对大家理解ListView的优化有所帮助。
标签:
原文地址:http://blog.csdn.net/it_zjyang/article/details/51596292