ListView是Android开发最常用的控件,适配器adapter是将要显示的数据映射到View中并添加到ListView中显示在实现ListView时,我们需要定义适配器如BaseAdapter、ArrayAdapter、CursorAdapter、SimpleAdapter等,并且重写其一下四个方法: 1 public int getCount(); 2 public Object getItem(int position) 3 public long getItemId(int position) 4 public View getView(int position, View convertView, ViewGroup parent)
我们需要定义一个View,用来显示每条信息,最后添加到ListView中。
其优化的原则是:空间和时间的相互转换或者自身转换,比如时间换时间,时间换空间,空间换空间,空间换时间。
比如我们定义了一个view文件:list_item_callsms.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:layout_marginTop="3dip"
android:layout_marginLeft="10dip"
android:id="@+id/tv_black_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="黑名单号码"
android:textColor="#88000000"
android:textSize="20sp" />
<TextView
android:layout_marginTop="3dip"
android:layout_marginLeft="10dip"
android:id="@+id/tv_black_mode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_black_number"
android:text="拦截模式"
android:textColor="#88000000"
android:textSize="15sp" />
</RelativeLayout>
private List<BlackNumberInfo> infos;
private class CallSMSSafeAdapter extends BaseAdapter {
@Override
public int getCount() {
// 返回多少条
return infos.size();
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return null;
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
//有多少个条目 这个方法就会被调用多少次
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//把一个布局文件转换成一个 view对象 每次都要调用 非常占用内存空间
View view = View.inflate(getApplicationContext(),
R.layout.list_item_callsms, null);
TextView tv_black_number = (TextView) view
.findViewById(R.id.tv_black_number);
TextView tv_black_mode = (TextView) view
.findViewById(R.id.tv_black_mode);
tv_black_number.setText(infos.get(position).getNumber());
String mode = "全部拦截";
if (infos.get(position).getMode().equals("1")) {
mode = "电话拦截";
} else if (infos.get(position).getMode().equals("2")) {
mode = "短信拦截";
}
tv_black_mode.setText(mode);
return view;
}
}
上面会出现一个问题,当要显示的数据集合超过100多条时(infos),当我们快速的滑动ListView时,界面会显示程序无响应,而LogCat中则不停的打印DVM虚拟机GC垃圾回收数据(实际是每次定义的View对象);这样别然严重的影响程序的性能和用户体验;
所以针对上述,可以对ListView使用的Adapter做两方面的优化:
1、 减少内存中View对象的创建次数个数。
ListView中的View在第一个可见的列表里是创建的(converView是为null的),当再次加载滑动时,会converView会复用之间创建的view,之后循环这个可见的View对象列表。
getViewTypeCount()
and
getItemViewType(int)
).
2、减小view中对应控件查找的次数。
每次查找view中的控件都是相当耗时的,相当于遍历了一遍view对应的xml树,所以我们只需在第一个遍历,以后直接复用。
因此我们可以创建一个View缓存,然后使用view的Tag存放的控件在内存中的地址。所以我们最多创建ViewList可见的长度个View,查找一次View中控件的id。
这样就能很大的提升程序的性能。
3、使用分批加载数据。
4、使用分页加载数据。
优化后的代码如下:
private class CallSMSSafeAdapter extends BaseAdapter {
@Override
public int getCount() {
// 返回多少条
return infos.size();
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return null;
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
//有多少个条目 这个方法就会被调用多少次
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
//把一个布局文件转换成一个 view对象 每次都要调用 非常占用内存空间
//1. 减小内存中View对象创建的个数
if(convertView == null){
//converView 为空时 才创建
convertView = View.inflate(getApplicationContext(),
R.layout.list_item_callsms, null);
holder = new ViewHolder();
//2. 减小View中控件查询的次数
holder.tv_number = (TextView) convertView
.findViewById(R.id.tv_black_number);
holder.tv_mode = (TextView) convertView
.findViewById(R.id.tv_black_mode);
//把ViewHolder保存在View的tag中
convertView.setTag(holder);
}else {
//获取View的Tag中的ViewHolder
holder = (ViewHolder) convertView.getTag();
}
holder.tv_number.setText(infos.get(position).getNumber());
String mode = "全部拦截";
if (infos.get(position).getMode().equals("1")) {
mode = "电话拦截";
} else if (infos.get(position).getMode().equals("2")) {
mode = "短信拦截";
}
holder.tv_mode.setText(mode);
return convertView;
}
}
/**
* 记录View中控件的内存地址 相当于一个记事本
* @author zty
*
*/
static class ViewHolder{
TextView tv_number;
TextView tv_mode;
}