自定义适配器的创建本身是为了实现内容丰富,布局多样的ListView,其中也就穿插了对ListView的优化。
步骤如下:
1:需要定义一个集合来存放ListView中的item对象,当然这个集合也可能是在网站上下载并解析出来的数据
2:自定义一个item的布局文件,这个布局文件就是每个item想要呈现出来的效果。
3:定义一个子类继承自BaseAdapter,并重写为实现的方法
getCount()--获取item的总数
getItem()--获取到某个item对象
getItemId()--获取该item对象的id(其实也可以理解为每个item有个index,对应集合中的某位置上的一组数据)
getView() -- 这是自定义适配器最重要的方法,不管是滑动还是第一次进入,加载每一个item都要执行的方法
4:创建一个内部类ViewHolder,并将item布局文件中的控件都定义成属性
5:在getView方法中完成往item布局文件填充集合数据的任务
6:通过ListView调用setAdapter绑定上适配器
为什么第4步中需要创建一个ViewHolder内部类呢?
因为如果不将item中的控件提取出来的话,每次执行getView方法的时候都要去执行查找控件的工作,查找工作也是比较费时的,所以其实创建内部类是为了减少findViewById操作的次数。
在第5步中,可以使用Recycler的构件,完成对View的复用,他的原理图:
这很好的解决了用户在大量滑动过程中过多的创建工作而卡顿甚至死掉的情况。这个构建的机制是只是新建必要的item,当用户将旧的item划出屏幕以外后,可以将该item获取到重新填充数据完成复用。
首先,当用户首次打开页面时,会完成新item的创建,但屏幕中能放置的item个数根据ListView的设置和屏幕大小来决定,但是可以确定的是,假设一个页面能刚好装下10个item,那么新建工作需要执行多少次呢?其实并不是,而是十一次,因为这里有一个临界点,就是当一个item还没有划出屏幕的时候,一个新的item已经滑进了页面中。
那么怎样实现复用呢?
其实getView方法中有一个参数是convertView,这个其实可以理解为就是一个装可复用item的容器,所以每次出现新item的时候,执行getView方法时都可以去判断一下这个容器是否为空,如果为空就新建item,并通过setTag来给这个新建的item打上标记,之后就可以通过这个标记找到他,如果不为空就可以通过getTag获取可复用的item。
下面结合代码:
public class MyAdapter extends BaseAdapter{
private List
private Context context;// 上下文
// 创建构造器,这样就可以通过构造器过去到需要的上下文还有存有数据集合
public MyAdapter(List
super();
this.data = data;
this.context = context;
}
// 获取item总数
@Override
public int getCount() {
return data.size();
}
// 获取某item
@Override
public Object getItem(int position) {
return data.get(position);
}
// 获取item的id
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
// 判断复用容器是否为空
// 如果为空的话执行新建工作
if(convertView == null){
// 加载布局文件
convertView = LayoutInflater.from(context).inflate(R.layout.item, null);
// 新建ViewHolder对象
viewHolder = new ViewHolder();
// 查找到布局中的子控件
viewHolder.tv_name = (TextView) convertView.findViewById(R.id.tv_name);
viewHolder.tv_classNo = (TextView) convertView.findViewById(R.id.tv_classNo);
// 将该新建出来的item打上标记
convertView.setTag(viewHolder);
} else {
// 如果复用容器中存在可复用的item,则直接调用getTag来获取该item
viewHolder = (ViewHolder) convertView.getTag();
}
// 获取到List集合中相应位置(position)上的一组数据
Student student = data.get(position);
// 将集合中数据分别设置到布局子控件中,完成数据的填充
viewHolder.tv_name.setText(student.getName());
viewHolder.tv_classNo.setText(student.getClassNo());
// 将该item返回
return convertView;
}
// 创建内部类,将Listview控件定义成属性
class ViewHolder{
public TextView tv_name,tv_classNo;
}
}
其实现在已经出现了一种更方便的控件--RecyclerView,他会自动完成复用,这样就使得我们所能做的更少了,但是他的原理和这个是保持一致的。
总结一下:
ListView的优化可以从三个方面下手:
1:使ListView的宽高固定,避免内容变化导致ListView渲染。
2:使用convertView减少对象的创建。
3:使用ViewHolder减少对象的查找。
刚开始写博客不久,希望大家多多鼓励。