分类: Java
2014-02-24 10:14:13
原文地址:Android Adapter详解 作者:hhllf61
Adapter是用来帮助填充数据的中间桥梁,比如通过它将数据填充到ListView, GridView, Gallery.而android 提供了几种Adapter:ArrayAdapter, BaseAdapter, CursorAdapter, HeaderViewListAdapter, ListAdapter, ResourceCursorAdapter, SimpleAdapter, SimpleCursorAdapter, SpinnerAdapter, WrapperListAdapter.
根据数据来源形式的不同可以选择不同的Adapter,比如数据来源于一个Arraylist 就使用BaseAdapter,SimpleAdapter,而数据来源于通过查询数据库获得Cursor那就使用
首先来看一下Adapter的体系结构:
一个Adapter的对象扮演一个桥梁的角色。这个桥梁连接着一个AdapterView和它所包含的数据。Adapter提供了一个通到数据项的途径。Adapter还负责为在数据集里的每个数据生项生成一个View。它有一个重要的方法:
public abstract View getView (int position,View convertView,ViewGroup parent)
。这个方法被setListAdapter(adapter)间接地调用。getView 方法的作用是得到一个View,这个view显示数据项里指定位置的数据,你可以或者手动创建一个view或者从一个XML layout中inflate。当这个view被inflated,它的父view(如GridView,ListView等)将要使用默认的layout参数,除非你用inflate(int,android.view.ViewGroup,boolean)方法来指定一个根view并防止附着在根上。
下面分别讲一下它的几个常见的子类:
ListAdapter接口:继承于Adapter。ListAdapter是一个ListView和list上的数据之间的桥梁。数据经常来自于一个Cursor,但这不是必须的。ListView能显示任何数据,只要它是被一个ListAdapter包装的。
BaseAdapter抽象类:是一个实现了既能在ListView(实现了ListAdapter接口)和Spinner(实现了Spinner接口)里用的Adapter类的一般基类。
ArrayAdapter类:一个管理这样的ListView的ListAdapter:这个ListView被一个数组所支持。这个数组可装任意对象。默认状态下,这个类预期能这样:提供的资源id与一个单独的TextView相关联。如果你想用一个更复杂的layout,就要用包含了域id的构造函数。这个域id能够与一个在更大的layout资源里的TextView相关联。它将被在数组里的每个对象的toString()方法所填满。你可以添加通常对象的lists或arrays。重写你对象的toString()方法来决定list里哪一个写有数据的text将被显示。如果想用一些其它的不同于TextView的view来显示数组(比如ImageViews),或想有一些除了toString()返回值所填在views里的以外的数据,你就要重写getView(int,View,ViewGroup)方法来返回你想要的View类型。
SimpleAdapter类:一个使静态数据和在XML中定义的Views对应起来的简单adapter。你可以把list上的数据指定为一个Map范型的ArrayList。ArrayList里的每一个条目对应于list里的一行。Maps包含着每一行的数据。你先要指定一个XML,这个XML定义了用于显示一行的view。你还要指定一个对应关系,这个对应关系是从Map的keys对应到指定的views。绑定数据到views发生在两个阶段:
如果一个simpleAdapter.ViewBinder是可用的,那么SetViewValue(android.view.View,Object,String)要被调用。如果返回true,那么绑定发生了。如果返回false,那么如下views将被按顺序地尝试:
~实现了Checkable的View(如CheckBox),预期的绑定值是boolen
~TextView,预期的绑定值是String,并且SetViewText方法被调用
~ImageView,预期的绑定值是一个资源的id或String。并且SetViewImage方法被调用
如果没有合适的绑定被发现,一个IllegalStateException被抛出。
下面是一个SimpleAdapter的例子:
/*--------------------mail.xml---------------------------*/
xmlns:android=""
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/MyListView">
/*--------------------simple_list_item_2.xml---------------------------*/
xmlns:android=""
android:id="@+id/widget0"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
android:id="@+id/ItemImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
android:id="@+id/ItemTitle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="TextView"
android:layout_x="50px"
android:textSize="15pt">
android:id="@+id/ItemText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="TextView"
android:layout_x="50px"
android:layout_y="40px">
android:id="@+id/ItemCheck"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_x="270px">
/*--------------------TestUIList.java---------------------------*/
public class TestUIList extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ListView list = (ListView) findViewById(R.id.MyListView);
ArrayList> lstImageItem = new ArrayList>();
String[] str1 = { "row one", "row two", "row three", "row four" };
String[] str2 = { "第一行", "第二行", "第三行", "第四行" };
for (int i = 0; i < str1.length; i++) {
HashMap map = new HashMap();
map.put("ItemImage", R.drawable.icon);
map.put("ItemTitle", str1[i]);
map.put("ItemText", str2[i]);
lstImageItem.add(map);
}
SimpleAdapter saImageItems = new SimpleAdapter(this,
lstImageItem,
R.layout.simple_list_item_2,
new String[] { "ItemImage", "ItemTitle", "ItemText" },
new int[] { R.id.ItemImage, R.id.ItemTitle, R.id.ItemText });
list.setAdapter(saImageItems);
}
}
如果你的ListView的每一行想实现被点击后有响应事件。最省事发的方法是继承一个ListActivity。ListActivity是一个这样的Activity:这个Activity能通过绑定到一个像array或cursor这样的数据源来显示一些items的list,并且当用户选了一个item时,能够暴露事件句柄。
ListActivity拥有一个ListView对象。这个ListView对象能够被绑定到不同的数据源,特别是一个数组或者一个拥有查询结果的Cursor。ListActivity有三种用法,分别是Binding,Screen Layout和Row Layout。下面仅讨论一下Screen Layout:
ListActivity有一个默认的layout。这个 layout是由一个在屏幕中央的、单独的、全屏的list构成。然而,如果你想的话,你可以通过在onCreate()里调用setContentView()方法来设置你自己的view layout的方式制定屏幕layout。要这样做,你自己的view必须包含一个id为“@android:id/android:list”(或者在代码中有list对象)。
随意地,当你制定这个view是空的时,你能够包含任何类型的view对象来显示。这个“空list”通知者必须有一个id“android:empty”。
注意,最后一定要调用setListAdapter(adapter)方法来把通过Adapter绑定了数据的这个List显示出来。setListAdapter方法间接调用了Adapter的getView方法,其作用是返回你想要的view类型。而且当点击listView里的item时,会根据getView重画这个ListView。例子可参见《Android SDK开发大全》中的“资源管理器“的例子。
想要实现事件监听,就要重写 protected void onListItemClick(ListView l, View v, int position,long id)方法。
想要把在XML中自定义了一行的view逐行显示在ListActivity中自定义的ListView中,并且在每行动态绑定数据的话,一般要自己写一个MyAdapter类,这个Adapter继承BaseAdapter并且其构造函数中至少有一个List参数来实现动态绑定数据。有两个重要的步骤:
1)重写getView方法,其中一重要步骤就是用items.get(position)方法来获得被传入的数据。其中items是一个List,它被赋了传入的List参数的值。position是这个数据在ListView中的行数。Get返回的是E类型.即List中的模板类型。
2)写一个内部类private class ViewHolder。这个内部类只有成员变量,它们就是你想在ListView中的一行里要显示的小View成分。
要想在Adapter中动态传入其它类的数据,就要在构造函数中再增加一个(或更多)List参数。
最后我们给出自己些的MyAdapter配合ListActivity实现监听事件的例子:
/*--------------------MyHome.java-----------------------*/
package com.li.android.myhome;
import java.util.ArrayList;
import java.util.List;
import android.app.AlertDialog;
import android.app.ListActivity;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.widget.ListView;
public class MyHome extends ListActivity
{
private List items = null;
protected void onCreate(Bundle icicle)
{
super.onCreate(icicle);
setContentView(R.layout.main);
items = new ArrayList();
String[] titles =
{ "默认主题", "主题-A", "主题-B", "中秋佳节", "粉红女郎", "花样年华" };
for (int i = 0; i < titles.length; i++)
{
String title = titles[i];
items.add(title);
}
setListAdapter(new MyAdapter(this, items));
}
@Override
protected void onListItemClick(ListView l, View v, int position,
long id)
{
new AlertDialog.Builder(MyHome.this).setItems(
R.array.items_my_dialog, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int whichcountry)
{
}
})
.show();
}
}
/*--------------------MyAdapter.java-----------------------*/
package com.li.android.myhome;
import java.util.List;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class MyAdapter extends BaseAdapter
{
private LayoutInflater mInflater;
private Bitmap icom_theme;
private Bitmap icon_selected32;
private List items;
public MyAdapter(Context context, List it)
{
mInflater = LayoutInflater.from(context);
items = it;
icom_theme = BitmapFactory.decodeResource(context.getResources(),
R.drawable.theme);
icon_selected32 = BitmapFactory.decodeResource(context
.getResources(), R.drawable.selected32);
}
public int getCount()
{
return items.size();
}
public Object getItem(int position)
{
return items.get(position);
}
public long getItemId(int position)
{
return position;
}
public View getView(int position, View convertView, ViewGroup parent)
{
ViewHolder holder;
convertView = mInflater.inflate(R.layout.file_row, null);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
holder.mTheme = (ImageView) convertView.findViewById(R.id.theme);
holder.mSelected32 = (ImageView) convertView
.findViewById(R.id.selected32);
holder.mTheme.setImageBitmap(icom_theme);
String title = items.get(position);
holder.text.setText(title);
holder.mSelected32.setImageBitmap(icon_selected32);
return convertView;
}
private class ViewHolder
{
TextView text;
ImageView mTheme;
ImageView mSelected32;
}
}
/*--------------------main.xml-----------------------*/
xmlns:android=""
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
/*-------------------- file_row.xml-----------------------*/
xmlns:android=""
android:id="@+id/widget0"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
android:id="@+id/text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_x="50px"
android:textSize="15pt">
android:id="@+id/selected32"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_x="270px">
/*--------------------string.xml-----------------------*/
Hello World, 仿熊猫
仿熊猫
应用主题
编辑主题
删除主题
dapter在Android中占据一个重要的角色,它是数据和UI(View)之间一个重要的纽带。在常见的View(ListView,GridView)等地方都需要用到Adapter。如图1直观的表达了Data、Adapter、View三者的关系。
图1 Adapter、数据、UI三者关系(PS:此图来自Google I/O)
一、Android中Adapter
图2:Android中Adapter类型层级图
由图2我们可以看到在Android中与Adapter有关的所有接口、类的完整层级图。在我们使用过程中可以根据自己的需求实现接口或者继承类进行一定的扩展。比较常用的有 BaseAdapter,ArrayAdapter,SimpleCursorAdapter等。
· BaseAdapter是一个抽象类,继承它需要实现较多的方法,所以也就具有较高的灵活性;
· ArrayAdapter支持泛型操作,通常需要实现getView方法,特殊情况下(结合数据row id),为了让ui事件相应处理方便点最好重写getItemId;
· SimpleCursorAdapter可以适用于简单的纯文字型ListView,它需要Cursor的字段和UI的id对应起来。如需要实现更复杂的UI也可以重写其他方法。
二、一个继承BaseAdapter的类的代码段
1: /**
2: * 歌曲列表适配器
3: *
4: * @version 2010-11-24 下午05:13:33
5: * @author Hal
6: */
7: public class AudioListAdapter extends BaseAdapter {
8:
9: private Context mContext;
10:
11: // 歌曲集合
12: private ArrayList
13:
14: public AudioListAdapter(Context mContext, ArrayList
15: this.mContext = mContext;
16: this.mAudios = mAudios;
17: }
18:
19: @Override
20: public int getCount() {
21: return mAudios != null ? mAudios.size() : 0;
22: }
23:
24: @Override
25: public Object getItem(int position) {
26: if ((mAudios != null && mAudios.size() > 0) && (position >= 0 && position < mAudios.size())) {
27: return mAudios.get(position);
28: }
29: return null;
30: }
31:
32: /**
33: * 如果集合中的对象数据来自数据库,建议此方法返回该对象在数据库中的ID
34: */
35: @Override
36: public long getItemId(int position) {
37: if ((mAudios != null && mAudios.size() > 0) && (position >= 0 && position < mAudios.size())) {
38: return mAudios.get(position).getId();
39: }
40: return position;
41: }
42:
43: @Override
44: public View getView(int position, View convertView, ViewGroup parent) {
45: //TODO 返回自定的View
46: }