本篇文章主要用来记录自己在开发当中常用的ListView控件适配器问题,因为在一个项目中有很多地方使用的listview控件的适配器都是可以复用的,但是我们不能在项目中到处新建Adapter,这样显得我们的代码冗余比较高,看起来比较繁琐,那么我们就需要抽取一个万能的BaseAdapter出来,这样在项目使用的地方只需要继承我们这个基类BaseAdapter就可以了,只需要传入我们关心的数据以及数据类型就可以了,那么今天就以此篇文章来记录项目开发中遇到的问题以及加深自己的印象,相信关于这样的文章早已烂大街了,但是那些都不是我自己的东西,今天写这样的文章记录纯属个人加深印象,高手绕道前行,如果文章中有不足或者错误的地方请前辈指正,谢谢!
- 最常见的办法
/**创建基类MyBasicAdapter,适配的数据类型设置为T(因为我们不知道具体是什么数据类型,只有子类才最 清楚自己需要适配什么样的数据类型,所以数据类型交给子类传递)
*/
public abstract class MyBaseAdapter<T> extends BaseAdapter {
public Context context;
public List<T> list;
// 有参构造方法:context上下文,list子类需要适配的数据以及数据类型T
public MyBaseAdapter(Context context,List<T> list) {
this.context = context;
this.list = list;
}
// 对外提供的公共方法:获取当前适配器中已经适配的数据集
public List<T> getList() {
return list;
}
// 对外提供的公共方法:设置当前适配器将要适配的数据集
public void setList(List<T> list) {
this.list = list;
}
// 实现BaseAdapter方法,返回适配的数据集个数,
@Override
public int getCount() {
if(list==null){
return 0;
}
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
// 实现BaseAdapter方法,返回当前条目ID:当前条目ID = 当前条目索引,
@Override
public long getItemId(int position) {
return position;
}
/** 这里我把最关键的一个方法抽象了出来,这样做的好处就是:因为这个方法需要一个Item布局convertView,而这个布局只有子类自己知道长什么样子,
父类是无法知道的,所以抽象出来交给子类自己去实现。
*/
@Override
public abstract View getView(int position, View convertView, ViewGroup parent) ;
}
到这里我们封装的万能适配器MyBaseAdapter已经完成了,这样在使用的时候继承我们的MyBaseAdapter就可以了,如果项目中还需要传入其他参数,那么我们只需要在构造方法里面直接添加就行,在这里给出一段使用的伪代码:
class adapter extends MyBaseAdapter<Person>{
public adapter (Context context, List<Person> list) {
super(context, list);
}
public View getView(int position, View convertView, ViewGroup parent) {
View itemView = View.inflate(R.layout.item_view,
parent, false);
return itemView;
}
}
这样是不是瞬间觉得我们的Adapter类干净清爽了许多呢。那么回想一下我们使用listview目的就是为了复用,所以,在getView方法里面可以继续优化,写到这里顺便提一下优化listview的两种方案(防止内存溢出和屏幕卡顿):
- item的布局复用(使用父类传递的convertView)
- 减少item布局里面的控件findviewById的次数(使用内部类ViewHold,当第一次查找完成将其绑定)
那么,我们继续优化,下面我只是简单的给出getView方法的优化,其他代码不变:
优化前首先我们想一下几个问题:
- getView里面都做了哪些事?
- 哪些操作父类做不到?
哪些操作只有子类才能做到?
(1)getView做了convertView的判断,用来判断是否有已经创建过的convertView复用,查找控件,控件绑定三件事;
(2)父类可以做convertView判断,以及ViewHold内部类的创建。
(3)子类必须做的:1.明确Item布局ID,2.查找控件,3.控件绑定。
分析完父类和子类各自的职责后,接下来就是代码实现的过程了交由子类去完成的事
public abstract int setLayoutId(); //返回向父类提供的Item布局的Id
public abstract View setWidgetData(int position, View convertView, ViewGroup parent); // 设置数据
public abstract void setHolder(View convertView);// 查找控件并绑定ViewHold
父类内部getView的实现逻辑
public View getView(int position, View convertView, ViewGroup parent){ int layoutId = setLayoutId(); // 调用子类传递的item布局id if (convertView == null) { // 判断是否有可复用的view convertView = View.inflate(BaseApplication.getInstance(), layoutId, null); holder = new ViewHolder(); setHolder(convertView); convertView.setTag(holder); // 这里的holder是MyBaseAdapter 的公有成员变量供子类直接调用 } else { holder = (ViewHolder) convertView.getTag(); // 复用convertView } return setWidgetData(position,convertView,parent) //调用子类传递已经设置数据后的view }
父类内部公有的内部类:ViewHolder
根据自己的项目需求添加自己的控件类型,注意:必须是public声明,供子类直接调用。public class ViewHolder { public TextView tv_01; public ImageView img_01; public RadioButton rb_01; }
到此整个MyBaseAdapter就抽取完成了。使用的时候直接继承MyBaseAdapter,实现父类的三个方法:
1. setLayoutId();
2. setWidgetData();
3. setHolder();