相信很多刚入门的Android开发者们都使用过ListView以及Adapter,虽然按照例程跑通了,但是对其原理还是一知半解,每次使用都得去查阅资料。其实Adapter原理很简单,只是使用起来稍微有一点点繁琐,今天我们就来一起梳理一下数据适配器Adapter的使用。
视频教程了解(新手福利):http://www.imooc.com/view/365
1. 什么是Adapter?
Adapter是连接后端数据和前端显示的适配器接口,是数据和UI(View)之间一个重要的纽带。在常见的View(ListView,GridView)等地方都需要用到Adapter。如下图直观的表达了Data、Adapter、View三者的关系:
图1 Adapter是数据源和UI之间的桥梁
适配器模式的作用:
- 降低代码耦合性
- 容易扩展
Android中所有的Adapter一览表:
图2. Android中的Adapter
2. BaseAdapter最常用的适配器
2.1. ListView显示与缓存的机制
提问:当我们有10亿个条目的时候怎么办,难道新创建一个新的布局并显示出来吗?答案肯定是“不”。Android会为你把布局缓存起来。
这一部分在Android中称呼为"Recycle - 回收利用"。以下为它的具体实现过程图。
图3. ListView显示机制
当你有一亿个条目的时候, 内存中存在的是:可看见的View +Recycle过的View。
当ListView第一次向适配器请求一个VIew的时候,convertView为null,因此需要新建一个convertView.
当ListView请求一个条目item1的VIew,并且item1已经超出屏幕之外,并进来一个相同类型的条目从底部进入到屏幕里面,这时convertVIew 不为null,而是等于item1。 你只需要获取新的数据装载到该View里面并返回回去。而不必要重新创建一个新的VIew。
总结:需要才显示,显示完就回收到缓存。
2.2 BaseAdapter的使用
public int getCount():适配器中数据集中数据的个数
public Object getItem(int postion):获取数据集中与指定索引对应的数据项
public long getItemId(int position):获取指定行对应的Id
public View getView (int position, View convertView, ViewGroup parent):获取每一个Item的显示内容
3. 使用步骤
1)创建主布局文件 activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
></ListView>
</RelativeLayout>
2)创建item:listview_item.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="match_parent"
android:orientation="vertical" >
<ImageView
android:id="@+id/imageview"
android:layout_width="60dp"
android:layout_height="60dp"
android:src="@drawable/ic_launcher"
/>
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_toEndOf="@+id/imageview"
android:text="Title"
android:gravity="center"
android:textSize="25sp"
/>
<TextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_toEndOf="@+id/imageview"
android:layout_toRightOf="@id/imageview"
android:layout_below="@+id/tv_title"
android:text="Content"
android:gravity="center_vertical"
android:textSize="20sp"
/>
</RelativeLayout>
3)创建数据源bean对象
public class ItemBean {
private int imageReId;
private String itemTitle;
private String itemContent;
public ItemBean(int imageReId, String itemTitle, String itemContent) {
super();
this.imageReId = imageReId;
this.itemTitle = itemTitle;
this.itemContent = itemContent;
}
//getter setter
}
4)创建逗比式适配器 MyAdaper.java
public class MyAdaper extends BaseAdapter {
private List<ItemBean> mList;
private LayoutInflater mInflater; //将layout转化为View对象,需要在构造方法中传入以初始化
public MyAdaper(Context context ,List<ItemBean> list) {
mList = list;
mInflater = LayoutInflater.from(context);//初始化Inflater
}
@Override
public int getCount() {
return mList.size();
}
@Override
public Object getItem(int arg0) {
return mList.get(arg0);
}
@Override
public long getItemId(int arg0) {
return arg0;
}
//返回每一项的显示内容——逗比式
@Override
public View getView(int arg0, View arg1, ViewGroup arg2) {
View view =mInflater.inflate(R.layout.listview_item, null);
ImageView imageView = (ImageView) view.findViewById(R.id.imageview);
TextView title = (TextView) view.findViewById(R.id.tv_title);
TextView content = (TextView) view.findViewById(R.id.tv_content);
ItemBean bean = mList.get(arg0);
imageView.setImageResource(bean.getImageReId());
title.setText(bean.getItemTitle());
content.setText(bean.getItemContent());
return view;
}
}
5)调用数据源
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
List<ItemBean> itemBeanList = new ArrayList<ItemBean>();
for (int i = 0; i < 20; i++) {
itemBeanList.add(new ItemBean(
R.drawable.ic_launcher,
"Iam title "+i,
"Iam content "+i
));
}
ListView listView = (ListView) findViewById(R.id.listview);
listView.setAdapter(new MyAdaper(this,itemBeanList));
}
逗比式返回了所有的内容,没有使用到ListView的缓存机制。没有任何优化处理,每次都创建新的View,设置控件。效率极为低下。
6)创建普通式适配器
if(convertView == null){
convertView = mInflater.inflate(R.layout.listview_item, null);
}
ImageView imageView = (ImageView) convertView.findViewById(R.id.imageview);
TextView title = (TextView) convertView.findViewById(R.id.tv_title);
TextView content = (TextView) convertView.findViewById(R.id.tv_content);
ItemBean bean = mList.get(arg0);
imageView.setImageResource(bean.getImageReId());
title.setText(bean.getItemTitle());
content.setText(bean.getItemContent());
return convertView;
普通式,利用了ListView的缓存特性,如果没有缓存才创建,能够节省一定的时间和内存,但是findViewByid依然会浪费大量的时间。
@Override
public View getView(int arg0, View convertView, ViewGroup arg2) {
//文艺式——ViewHolder
ViewHolder viewHolder;
if(convertView == null){
viewHolder = new ViewHolder();
convertView = mInflater.inflate(R.layout.listview_item, null);
viewHolder.imageView = (ImageView) convertView.findViewById(R.id.imageview);
viewHolder.title = (TextView) convertView.findViewById(R.id.tv_title);
viewHolder.content = (TextView) convertView.findViewById(R.id.tv_content);
convertView.setTag(viewHolder);
}else{
viewHolder = (ViewHolder) convertView.getTag();
}
ItemBean bean = mList.get(arg0);
viewHolder.imageView.setImageResource(bean.getImageReId());
viewHolder.title.setText(bean.getItemTitle());
viewHolder.content.setText(bean.getItemContent());
return convertView;
}
class ViewHolder{
public ImageView imageView;
public TextView title;
public TextView content;
}
文艺式的适配器,不仅利用了ListView的缓存,更通过ViewHolder类来实现显示数据的视图的缓存避免多次通过findViewById寻找控件。
4. 总结一下
步骤:
l 创建Bean对象,用于封装数据
l 在构造方法中初始化用于映射的数据List
l 创建ViewHolder类,创建布局映射关系
l 判断converView,为空则创建,并设置tag,否则通过tag来取出ViewHolder
l 给viewHolder中的控件设置数据。