Android是完全遵循MVC模式设计的框架,Activity是Controller,layout是View,因为layout五花八门,很多数据都不能直接绑定上去,所以Android引入了Adapter这个机制作为复杂数据的展示的转换载体,所以各种Adapter只不过是转换的方式和能力不一样而已。
Android中所有的Adapter一览:
由图可以看到在Android中与Adapter有关的所有接口、类的完整层级图。在我们使用过程中可以根据自己的需求实现接口或者继承类进行一定的扩展。比较常用的有 BaseAdapter,SimpleAdapter,ArrayAdapter,SimpleCursorAdapter等。
- BaseAdapter是一个抽象类,继承它需要实现较多的方法,所以也就具有较高的灵活性;
- ArrayAdapter支持泛型操作,最为简单,只能展示一行字。
- SimpleAdapter有最好的扩充性,可以自定义出各种效果。
- SimpleCursorAdapter可以适用于简单的纯文字型ListView,它需要Cursor的字段和UI的id对应起来。如需要实现更复杂的UI也可以重写其他方法。可以认为是SimpleAdapter对数据库的简单结合,可以方便地把数据库的内容以列表的形式展示出来。
Adapter是将数据绑定到UI界面上的桥接类。Adapter负责创建显示每个项目的子View和提供对下层数据的访问。 在多数情况下,你不需要创建自己的Adapter。Android提供了一系列Adapter来将数据绑定到UI Widget上。
因为Android负责提供数据和选择用于显示每个项目的View,所以Adapter能快速地修改要绑定的控件的外观和功能。
下面的列表显示了两个最有用和最通用的本地Adapter:
❑ ArrayAdapter
ArrayAdapter是一个绑定View到一组对象的通用类。默认情况下,ArrayAdapter绑定每个对象的toString值到在layout中预先定义的TextView控件上。可变通的,构造函数允许你使用更加复杂的layout或者通过重写getView方法来扩展类从而使用TextView的替代物(如ImageView或嵌套的layout)。
❑ SimpleCursorAdapter
SimpleCursorAdapter绑定View到Content Provider查询返回的游标上。指定一个XML layout定义,然后将数据集中的每一列的值绑定到layout中的一个View上。
使用Adapter进行数据绑定:
将Adapter应用到继承自AdapterView类上,你需要调用View的setAdapter方法,传入一个Adapter实例,如下面的片段所示:
ArrayList<String> myStringArray = new ArrayList<String>(); ArrayAdapter<String> myAdapterInstance; int layoutID = android.R.layout.simple_list_item_1; myAdapterInstance = new ArrayAdapter<String>(this, layoutID, myStringArray); myListView.setAdapter(myAdapterInstance);
这个片段显示了最简单的情况,将数组中的字符串绑定到ListView中用于显示每个项目的简单TextView控件上。
2.应用案例
1)ArrayAdapter
列表的显示需要三个元素:
a.ListVeiw 用来展示列表的View。
b.适配器 用来把数据映射到ListView上的中介。
c.数据 具体的将被映射的字符串,图片,或者基本组件。
案例一
1 public class ArrayAdapterActivity extends ListActivity { 2 @Override 3 public void onCreate(Bundle savedInstanceState) { 4 super.onCreate(savedInstanceState); 5 //列表项的数据 6 String[] strs = {"1","2","3","4","5"}; 7 ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_expandable_list_item_1,strs); 8 setListAdapter(adapter); 9 } 10 }
案例二
1 public class MyListView extends Activity { 2 3 private ListView listView; 4 //private List<String> data = new ArrayList<String>(); 5 @Override 6 public void onCreate(Bundle savedInstanceState){ 7 super.onCreate(savedInstanceState); 8 9 listView = new ListView(this); 10 listView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_expandable_list_item_1,getData())); 11 setContentView(listView); 12 } 13 14 private List<String> getData(){ 15 16 List<String> data = new ArrayList<String>(); 17 data.add("测试数据1"); 18 data.add("测试数据2"); 19 data.add("测试数据3"); 20 data.add("测试数据4"); 21 22 return data; 23 } 24 }
上面代码使用了ArrayAdapter(Context context, int textViewResourceId, List<T> objects)来装配数据,要装配这些数据就需要一个连接ListView视图对象和数组数据的适配器来两者的适配工作,ArrayAdapter的构造需要三个参数,依次为this,布局文件(注意这里的布局文件描述的是列表的每一行的布局,android.R.layout.simple_list_item_1是系统定义好的布局文件只显示一行文字,数据源(一个List集合)。同时用setAdapter()完成适配的最后工作。效果图如下:
2)SimpleAdapter
simpleAdapter的扩展性最好,可以定义各种各样的布局出来,可以放上ImageView(图片),还可以放上Button(按钮),CheckBox(复选框)等等。下面的代码都直接继承了ListActivity,ListActivity和普通的Activity没有太大的差别,不同就是对显示ListView做了许多优化,方面显示而已。
案例一
simple.xml
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:orientation="vertical" 4 android:layout_width="fill_parent" 5 android:layout_height="fill_parent" 6 > 7 <ImageView 8 android:id="@+id/img" 9 android:layout_width="wrap_content" 10 android:layout_height="wrap_content" 11 android:layout_margin="5dp" 12 /> 13 <TextView 14 android:id="@+id/title" 15 android:layout_width="wrap_content" 16 android:layout_height="wrap_content" 17 android:textColor="#ffffff" 18 android:textSize="20sp" 19 /> 20 </LinearLayout>
1 public class SimpleAdapterActivity extends ListActivity { 2 @Override 3 public void onCreate(Bundle savedInstanceState) { 4 super.onCreate(savedInstanceState); 5 6 SimpleAdapter adapter = new SimpleAdapter(this, getData(), R.layout.simple, new String[] { "title", "img" }, new int[] { R.id.title, R.id.img }); 7 setListAdapter(adapter); 8 } 9 10 private List<Map<String, Object>> getData() { 11 //map.put(参数名字,参数值) 12 List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(); 13 Map<String, Object> map = new HashMap<String, Object>(); 14 map.put("title", "摩托罗拉"); 15 map.put("img", R.drawable.icon); 16 list.add(map); 17 18 map = new HashMap<String, Object>(); 19 map.put("title", "诺基亚"); 20 map.put("img", R.drawable.icon); 21 list.add(map); 22 23 map = new HashMap<String, Object>(); 24 map.put("title", "三星"); 25 map.put("img", R.drawable.icon); 26 list.add(map); 27 return list; 28 } 29 30 }
案例二
下面的程序是实现一个带有图片的类表。首先需要定义好一个用来显示每一个列内容的xml,vlist.xml
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" 3 android:layout_height="fill_parent"> 4 <ImageView android:id="@+id/img" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="5px"/> 5 <LinearLayout android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="wrap_content"> 6 <TextView android:id="@+id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" 7 android:textColor="#FFFFFFFF" android:textSize="22px" /> 8 <TextView android:id="@+id/info" android:layout_width="wrap_content" android:layout_height="wrap_content" 9 android:textColor="#FFFFFFFF" android:textSize="13px" /> 10 </LinearLayout> 11 </LinearLayout>
1 public class MyListView3 extends ListActivity { 2 // private List<String> data = new ArrayList<String>(); 3 @Override 4 public void onCreate(Bundle savedInstanceState) { 5 super.onCreate(savedInstanceState); 6 7 SimpleAdapter adapter = new SimpleAdapter(this,getData(),R.layout.vlist, 8 new String[]{"title","info","img"}, 9 new int[]{R.id.title,R.id.info,R.id.img}); 10 setListAdapter(adapter); 11 } 12 13 private List<Map<String, Object>> getData() { 14 List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(); 15 16 Map<String, Object> map = new HashMap<String, Object>(); 17 map.put("title", "G1"); 18 map.put("info", "google 1"); 19 map.put("img", R.drawable.i1); 20 list.add(map); 21 22 map = new HashMap<String, Object>(); 23 map.put("title", "G2"); 24 map.put("info", "google 2"); 25 map.put("img", R.drawable.i2); 26 list.add(map); 27 28 map = new HashMap<String, Object>(); 29 map.put("title", "G3"); 30 map.put("info", "google 3"); 31 map.put("img", R.drawable.i3); 32 list.add(map); 33 34 return list; 35 } 36 }
使用simpleAdapter的数据用一般都是HashMap构成的List,list的每一节对应ListView的每一行。HashMap的每个键值数据映射到布局文件中对应id的组件上。因为系统没有对应的布局文件可用,我们可以自己定义一个布局vlist.xml。下面做适配,new一个SimpleAdapter参数一次是:this,布局文件(vlist.xml),HashMap的 title 和 info,img。布局文件的组件id,title,info,img。布局文件的各组件分别映射到HashMap的各元素上,完成适配。
运行效果如下图:
3)SimpleCursorAdapter
1 public class SimpleCursorAdapterActivity extends ListActivity { 2 @Override 3 public void onCreate(Bundle savedInstanceState) { 4 super.onCreate(savedInstanceState); 5 //获得一个指向系统通讯录数据库的Cursor对象获得数据来源 6 Cursor cur = getContentResolver().query(People.CONTENT_URI, null, null, null, null); 7 startManagingCursor(cur); 8 //实例化列表适配器 9 10 ListAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1, cur, new String[] {People.NAME}, new int[] {android.R.id.text1}); 11 setListAdapter(adapter); 12 } 13 }
一定要以数据库作为数据源的时候,才能使用SimpleCursorAdapter,这里特别需要注意的一点是:不要忘了在AndroidManifest.xml文件中加入权限
<uses-permission android:name="android.permission.READ_CONTACTS"></uses-permission>
效果如下:
4)BaseAdapter
有时候,列表不光会用来做显示用,我们同样可以在在上面添加按钮。添加按钮首先要写一个有按钮的xml文件,然后自然会想到用上面的方法定义一个适配器,然后将数据映射到布局文件上。但是事实并非这样,因为按钮是无法映射的,即使你成功的用布局文件显示出了按钮也无法添加按钮的响应,这时就要研究一下ListView是如何现实的了,而且必须要重写一个类继承BaseAdapter。下面的示例将显示一个按钮和一个图片,两行字如果单击按钮将删除此按钮的所在行。并告诉你ListView究竟是如何工作的。
vlist2.xml
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" 3 android:layout_height="fill_parent"> 4 <ImageView android:id="@+id/img" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="5px"/> 5 <LinearLayout android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="wrap_content"> 6 <TextView android:id="@+id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" 7 android:textColor="#FFFFFFFF" android:textSize="22px" /> 8 <TextView android:id="@+id/info" android:layout_width="wrap_content" android:layout_height="wrap_content" 9 android:textColor="#FFFFFFFF" android:textSize="13px" /> 10 </LinearLayout> 11 12 <Button android:id="@+id/view_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" 13 android:text="@string/s_view_btn" android:layout_gravity="bottom|right" /> 14 </LinearLayout>
1 /** 2 * @author 3 * 4 */ 5 public class MyListView4 extends ListActivity { 6 7 8 private List<Map<String, Object>> mData; 9 10 @Override 11 public void onCreate(Bundle savedInstanceState) { 12 super.onCreate(savedInstanceState); 13 mData = getData(); 14 MyAdapter adapter = new MyAdapter(this); 15 setListAdapter(adapter); 16 } 17 18 private List<Map<String, Object>> getData() { 19 List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(); 20 21 Map<String, Object> map = new HashMap<String, Object>(); 22 map.put("title", "G1"); 23 map.put("info", "google 1"); 24 map.put("img", R.drawable.i1); 25 list.add(map); 26 27 map = new HashMap<String, Object>(); 28 map.put("title", "G2"); 29 map.put("info", "google 2"); 30 map.put("img", R.drawable.i2); 31 list.add(map); 32 33 map = new HashMap<String, Object>(); 34 map.put("title", "G3"); 35 map.put("info", "google 3"); 36 map.put("img", R.drawable.i3); 37 list.add(map); 38 39 return list; 40 } 41 42 // ListView 中某项被选中后的逻辑 43 @Override 44 protected void onListItemClick(ListView l, View v, int position, long id) { 45 46 Log.v("MyListView4-click", (String)mData.get(position).get("title")); 47 } 48 49 /** 50 * listview中点击按键弹出对话框 51 */ 52 public void showInfo(){ 53 new AlertDialog.Builder(this) 54 .setTitle("我的listview") 55 .setMessage("介绍...") 56 .setPositiveButton("确定", new DialogInterface.OnClickListener() { 57 @Override 58 public void onClick(DialogInterface dialog, int which) { 59 } 60 }) 61 .show(); 62 63 } 64 65 66 67 public final class ViewHolder{ 68 public ImageView img; 69 public TextView title; 70 public TextView info; 71 public Button viewBtn; 72 } 73 74 75 public class MyAdapter extends BaseAdapter{ 76 77 private LayoutInflater mInflater; 78 79 80 public MyAdapter(Context context){ 81 this.mInflater = LayoutInflater.from(context); 82 } 83 @Override 84 public int getCount() { 85 // TODO Auto-generated method stub 86 return mData.size(); 87 } 88 89 @Override 90 public Object getItem(int arg0) { 91 // TODO Auto-generated method stub 92 return null; 93 } 94 95 @Override 96 public long getItemId(int arg0) { 97 // TODO Auto-generated method stub 98 return 0; 99 } 100 101 @Override 102 public View getView(int position, View convertView, ViewGroup parent) { 103 104 ViewHolder holder = null; 105 if (convertView == null) { 106 107 holder=new ViewHolder(); 108 109 convertView = mInflater.inflate(R.layout.vlist2, null); 110 holder.img = (ImageView)convertView.findViewById(R.id.img); 111 holder.title = (TextView)convertView.findViewById(R.id.title); 112 holder.info = (TextView)convertView.findViewById(R.id.info); 113 holder.viewBtn = (Button)convertView.findViewById(R.id.view_btn); 114 convertView.setTag(holder); 115 116 }else { 117 118 holder = (ViewHolder)convertView.getTag(); 119 } 120 121 122 holder.img.setBackgroundResource((Integer)mData.get(position).get("img")); 123 holder.title.setText((String)mData.get(position).get("title")); 124 holder.info.setText((String)mData.get(position).get("info")); 125 126 holder.viewBtn.setOnClickListener(new View.OnClickListener() { 127 128 @Override 129 public void onClick(View v) { 130 showInfo(); 131 } 132 }); 133 134 135 return convertView; 136 } 137 138 } 139 }
下面将对上述代码,做详细的解释,listView在开始绘制的时候,系统首先调用getCount()函数,根据他的返回值得到listView的长度(这也是为什么在开始的第一张图特别的标出列表长度),然后根据这个长度,调用getView()逐一绘制每一行。如果你的getCount()返回值是0的话,列表将不显示同样return 1,就只显示一行。
系统显示列表时,首先实例化一个适配器(这里将实例化自定义的适配器)。当手动完成适配时,必须手动映射数据,这需要重写getView()方法。系统在绘制列表的每一行的时候将调用此方法。getView()有三个参数,position表示将显示的是第几行,covertView是从布局文件中inflate来的布局。我们用LayoutInflater的方法将定义好的vlist2.xml文件提取成View实例用来显示。然后将xml文件中的各个组件实例化(简单的findViewById()方法)。这样便可以将数据对应到各个组件上了。但是按钮为了响应点击事件,需要为它添加点击监听器,这样就能捕获点击事件。至此一个自定义的listView就完成了,现在让我们回过头从新审视这个过程。系统要绘制ListView了,他首先获得要绘制的这个列表的长度,然后开始绘制第一行,怎么绘制呢?调用getView()函数。在这个函数里面首先获得一个View(实际上是一个ViewGroup),然后再实例并设置各个组件,显示之。好了,绘制完这一行了。那再绘制下一行,直到绘完为止。在实际的运行过程中会发现listView的每一行没有焦点了,这是因为Button抢夺了listView的焦点,只要布局文件中将Button设置为没有焦点就OK了。
效果如下: