标签:
在我的上一篇博客《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