相信ListView、GridView大家都有用过,数据绑定时使用Adapter,但用归用,我们为什么要这样用呢?为什么TextView,ImageView就不用呢?
打开 Android文档 ,查看 View -> AdapterView,可以发现ListView,GridView等它们是继承AdapterView的,这就是它们使用Adpater的理由?
好吧,废话不多说,Adapter适配器,那我们知道它是使用了 Java设计模式 中的 适配器模式
适配器模式: 是把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
不知道你是否对上面那句话有什么深刻的理解,网上看过很多关于Android适配器的介绍,总体感觉就是:嗯,你写的东西我看懂了,但是我写ListView时还是不明白它为什么不能处理数据的绑定。
进入主题~
我们常说,ListView、GridView等等不知道如何处理数据绑定,需要借助Adpater去实现,意思就是说:我们有 ListView类,DataSet(数据集)类,但是ListView并没有listView.setDataSet(dataSet)这样的接口,就是上文中说的接口不匹配。
那么为什么不给ListView添加一个这样的接口(方法、API)呢?看TextView.setText(text)、ImageView.setImageResource(resId)用着多方便(这里就体现出为什么TextView不使用Adpter去绑定数据,它有相应的接口setText(),它知道如何处理数据)。
我们知道ListView装载子布局,子布局中摆放不同的控件
比如,QQ消息列表,一个ListView(它不一定用的是ListView,现在大多使用RecyclerView。你懂我意思就行)。装载了和每一个好友聊天的子布局,子布局中有头像(ImageView),好友名称(TextView),消息内容(TextView) 等等
再比如,QQ空间的动态信息,ListView装载一个子布局显示每一条动态,那这个子布局中的内容是不是和QQ消息子布局的内容不太一样?
正是因为ListView中子布局使用控件的不同,控件的数据绑定不同,ListView并不知道我们具体项目中使用怎样的子布局,需要调用哪些接口(方法)进行数据绑定,因此,ListView无法像TextView那样提供一个数据绑定的接口。
注:ListView有个entries属性,它能处理的数据只有string-array
视图类(ListView),数据类(DataSet)接口不匹配,那不行,这时Adapter横空出世。
它是如何解决接口不匹配同时实现数据绑定的呢?看下 Adapter的文档 ,再来看下项目中适配器长什么样?
// 这里我们选择继承BaseAdapter类
public class MyAdapter extends BaseAdapter {
//返回ListView要显示的条目数
//比如,listView显示10条信息,我们就返回10
@Override
public int getCount() {
return 0;
}
//返回ListView中position位置的实体数据
@Override
public Object getItem(int position) {
return null;
}
// 返回ListView中的某一项的ID,即某一项的position
@Override
public long getItemId(int position) {
return 0;
}
//返回一个View,这个View就是我们的子布局
//在这个方法中我们处理ListView中子布局的数据绑定
@Override
public View getView(int position, View view, ViewGroup viewGroup) {
return null;
}
}
可以看到使用哪个适配器,是和数据源有关,通常,使用BaseAdapter就可以。
那有了大概的样子,看下具体的方法内我们要写哪些内容吧。
public class MyAdapter extends BaseAdapter {
//上下文对象,getView中加载子布局时用到
private Context mContext;
// 这里是我们的数据,我们把每一项的实体数据存放在List集合中
private List mDataSet;
//构造器,创建Adapter时传入Context和具体的数据集合
public MyAdapter(Context context, List dataSet) {
mContext = context;
mDataSet = dataSet;
}
@Override
public int getCount() {
//返回集合中数据的条数
return mDataSet.size();
}
@Override
public Object getItem(int position) {
//返回集合中position位置的实体类对象
return mDataSet.get(position);
}
@Override
public long getItemId(int position) {
//返回ListView中的position
return position;
}
......
}
public class MyAdapter extends BaseAdapter {
......
@Override
public View getView(int position, View view, ViewGroup parent) {
//我们在 res/layout 文件夹中新建了一个layout文件,叫做:item_list.xml
//加载ListView要装载的子布局
View itemView = LayoutInflater.from(mContext)
.inflate(R.layout.item_list, parent, false);
//获取数据集合中position位置的数据实体对象
ItemEntry item = mDataSet.get(position);
//实例化控件并绑定数据
ImageView iv = (ImageView) itemView.findViewById(R.id.iv_avatar);
iv.setImageResource(item.getImage());
TextView tv = (TextView) itemView.findViewById(R.id.tv_name);
tv.setText(item.getName());
//最后返回我们的子布局
return itemView;
}
}
getView方法是真正解决问题的方法,加载视图,绑定数据
至此,我们的适配器就大概懂了吧? 适配器模式是不是也稍稍理解了一点?
最后两点
现在项目中常使用RecyclerView去实现ListView,GridView等等列表视图的效果,RecyclerView不继承AdapterView但是它内部封装了Adapter,对Adapter的理解是通用的
在getView()方法中我们直接使用了findViewById(),这样性能不好,通常结合ViewHolder去使用,关于ViewHolder没有什么难理解的地方,自己搜索下就可以了。