首页 > 代码库 > 详解listview的四种适配器模式
详解listview的四种适配器模式
ListView是Android开发中比较常用的一个组件,它以列表的形式展示具体内容,并且能够根据数据的长度自适应显示。比如说我们手机里的通讯录就使用到了ListView显示联系人信息。ListView同时也是所有Android UI控件中最为麻烦的控件,之所以麻烦就是因为它的各种的适配器特别麻烦。
创建ListView有两种方式:
1、直接创建ListView
2、让Activity继承ListActivity
列表的显示需要三个元素:
1.ListVeiw :用来展示列表的View。
2.适配器 :用来把数据映射到ListView上的中介。
3.数据集 :具体的将被映射的字符串,图片,或者基本组件。
根据列表的适配器类型,列表分为四种,ArrayAdapter、SimpleAdapter、SimpleCursorAdapter以及自定义Adapter。
其中以ArrayAdapter最为简单,只能展示一行字。SimpleAdapter有最好的扩充性,可以自定义出各种效果。 SimpleCursorAdapter可以认为是SimpleAdapter对数据库的简单结合,可以方面的把数据库的内容以列表的形式展示出来。
下面我们就通过几个简单的例子讲解一下通过各种适配器来构建ListView。
1、1使用ArrayAdatpter构建ListView
新建一个Android项目:ListViewDemo,同时新建一个类ListViewDemoActivity继承Activity,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | package com.liuzhichao.listview; import android.app.Activity; import android.os.Bundle; import android.widget.ArrayAdapter; import android.widget.ListView; public class ListViewDemoActivity extends Activity { //定义一个ListView private ListView mListView; //定义一个String数组,数组里的数据就是ListView里的一项 private String[] items={ "1、ArrayAdapter_List" , "2、SimpleAdapter_List" , "3、SimpleCursorAdapter_List" , "4、MyAdapter_List" }; @Override public void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); //new一个ListView mListView = new ListView( this ); //通过setAdapter构建一个ArrayAdapter将items与mListView"绑定" mListView.setAdapter( new ArrayAdapter<String>( this , android.R.layout.simple_list_item_1, items)); //显示mListView setContentView(mListView); } } |
上面代码使用了ArrayAdapter(Context context, int textViewResourceId, List<T> objects)来装配数据,要装配这些数据就需要一个连接ListView视图对象和数组数据的适配器来两者的适配工作,ArrayAdapter的构造需要三个参数,依次为this,布局文件(注意这里的布局文件描述的是列表的每一行的布局,android.R.layout.simple_list_item_1是系统定义好的布局文件只显示一行文字,数据源(一个List集合)。同时用setAdapter()完成适配的最后工作。运行后的现实结构如下图:
1、2使用SimpleAdapter构建ListView
使用simpleAdapter的数据用一般都是HashMap构成的List,list的每一节对应ListView的每一行。HashMap的每个键值数据映射到布局文件中对应id的组件上。下面我们使用SimpleAdapter模拟一个通讯录。
因为系统没有对应的布局文件可用,我们可以自己定义一个布局info.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | <?xml version= "1.0" encoding= "utf-8" ?> <RelativeLayout xmlns:android= "http://schemas.android.com/apk/res/android" android:layout_width= "fill_parent" android:layout_height= "fill_parent" > <ImageView android:id= "@+id/info_img" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:layout_marginLeft= "3dp" android:src=http://www.mamicode.com/ "@drawable/ic_launcher" /> <TextView android:id= "@+id/info_name" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:layout_toRightOf= "@id/info_img" android:layout_marginTop= "5dp" /> <TextView android:id= "@+id/info_phone" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:layout_below= "@id/info_name" android:layout_toRightOf= "@id/info_img" android:layout_alignBaseline= "@id/info_img" /> <TextView android:id= "@+id/info_region" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:layout_toRightOf= "@id/info_phone" android:layout_alignBaseline= "@id/info_phone" android:layout_marginLeft= "10dip" /> </RelativeLayout> |
新建一个SimpleAdapterListView继续Activity,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | package com.liuzhichao.listview; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import android.app.Activity; import android.os.Bundle; import android.widget.ListView; import android.widget.SimpleAdapter; public class SimpleAdapterListView extends Activity{ //分别定义通讯录中的用户名、电话、地区等信息 private String[] info_Names={ "李方刚" , "李玉刚" , "李刚" , "刘产" , "扬伟" , "范剑" }; private String[] info_Phones={ "13844445144" , "13844444444" , "13444445144" , "13544445144" , "13644445144" , "13744445144" }; private String[] info_Regions={ "火星" , "水星" , "木星" , "月球" , "美国" , "未知地区" }; //定义一个ArrayList数组,每一条数据对应通讯录中的一个联系人信息 private ArrayList<Map<String,Object>> mInfos= new ArrayList<Map<String,Object>>(); //定义一个ListView private ListView mListView; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super .onCreate(savedInstanceState); //new一个ListView mListView = new ListView( this ); //添加联系人信息 for (int i=0;i<info_Names.length;i++){ Map<String,Object> item = new HashMap<String,Object>(); item.put( "img" , R.drawable.contact_img); item.put( "name" , info_Names[i]); item.put( "phone" , info_Phones[i]); item.put( "region" , info_Regions[i]); mInfos.add(item); } //定义一个SimpleAdapter SimpleAdapter adapter = new SimpleAdapter( this , mInfos, R.layout.info, new String[]{ "img" , "name" , "phone" , "region" }, new int[]{R.id.info_img,R.id.info_name,R.id.info_phone,R.id.info_region}); //设置mListView的适配器为adapter mListView.setAdapter(adapter); setContentView(mListView); } } |
使用simpleAdapter的数据用一般都是HashMap构成的List,list的每一节对应ListView的每一行。HashMap的每个键 值数据映射到布局文件中对应id的组件上。因为系统没有对应的布局文件可用,我们可以自己定义一个布局info.xml。运行效果如下图:
我相信肯定有人对new SimpleAdapter()中的参数有一些疑问,下面我们就来看一下SimpleAdapter的构造函数:SimpleAdapter(Contextcontext, List <? extends Map <String, ?>> data, int resource, String[] from, int[] to) ,context相信不用解释了,假设将SimpleAdapter用于ListView。那么ListView的每一个列表项就是resource参数值指定的布局。而data参数就是要加载到ListView中的数据。那么from和to呢?在加载列表项时,需要通过组件的id和data参数中List元素中的Map对象对应。因此,from参数为Map对象的key,而to表示组件的id,例如,本例中的参数值为from=new String[]{“img”,”name”,”phone”,”region”},to=new int[]{R.id.info_img,R.id.info_name,R.id.info_phone,R.id.info_region}),意思就是将Map对象中key为”img”的value绑定到R.id.info_img,将Map对象中key为”name”的value绑定到R.id.info_name,phone、region也类似。所以from和to中的参数是一一对应的关系。同时 from又是对应的Map中的key,to又是对应布局文件中相应组件的ID。
1、3使用SimpleCursorAdapter 构建ListView
相比SimpleAdapter,SimpleCursorAdapter 就是方便把从游标得到的数据进行列表显示,并可以把指定的列映射到对应的组件中。SimpleCursorAdapter的构造函数与SimpleAdapter的区别就是多了一个Cursor c:SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to),Cursor就是游标,如果你不清楚游标的概念,就想像成数据查询后的一个结果集。下面是一个通过SimpleCursorAdapter 使用ListView显示系统通讯录中联系人的例子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | package com.liuzhichao.listview; import android.app.Activity; import android.database.Cursor; import android.os.Bundle; import android.provider.ContactsContract.Contacts; import android.widget.ListView; import android.widget.SimpleCursorAdapter; public class SimpleCursorAdapterActivity extends Activity { private ListView mListView; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super .onCreate(savedInstanceState); mListView = new ListView( this ); Cursor cursor= this .getContentResolver().query(Contacts.CONTENT_URI, null , null , null , null ); SimpleCursorAdapter adapter= new SimpleCursorAdapter( this , android.R.layout.simple_expandable_list_item_2, cursor, new String[]{Contacts.DISPLAY_NAME}, new int[]{android.R.id.text1}); mListView.setAdapter(adapter); setContentView(mListView); } } |
注意:在读取系统通讯录时,需要如下权限:
1 | <uses-permission android:name= "android.permission.READ_CONTACTS" /> |
左图上是系统通讯录中的联系人,右图是运行的结果。
1、4使用自定义Adapter构建ListView
当我们使用系统自带的ArrayAdapter、SimpleAdapter和SimpleCursorAdapter适配器时,对于事件的响应只能局限在一个行单位。假设一行里面有一个按钮和一个图片控件,它们之间的响应操作是不一样的。若采用系统自带的适配器,就不能精确到每个控件的响应事件。这时,我们一般采取自定义适配器来实现这个比较精确地请求。我们再新建一个MyAdapterListActivity继承Activity,使用自定义适配器来实现SimpleAdapterListView中的效果,并新增一个多选框和按钮。效果如下:
MyAdapterListActivity.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | package com.liuzhichao.listview; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; public class MyAdapterListActivity extends Activity { //分别定义通讯录中的用户名、电话、地区等信息 private String[] info_Names={ "史珍香" , "赖月京" , "秦寿生" , "刘产" , "扬伟" , "范剑" }; private String[] info_Phones={ "13844445144" , "13844444444" , "13444445144" , "13544445144" , "13644445144" , "13744445144" }; private String[] info_Regions={ "火星" , "水星" , "木星" , "月球" , "美国" , "未知" }; //定义一个ArrayList数组,每一条数据对应通讯录中的一个联系人信息 private ArrayList<Map<String,Object>> mInfos= new ArrayList<Map<String,Object>>(); //定义一个ListView private ListView mListView; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super .onCreate(savedInstanceState); //new一个ListView mListView = new ListView( this ); //添加联系人信息 for (int i=0;i<info_Names.length;i++){ Map<String,Object> item = new HashMap<String,Object>(); item.put( "img" , R.drawable.contact_img); item.put( "name" , info_Names[i]); item.put( "phone" , info_Phones[i]); item.put( "region" , info_Regions[i]); mInfos.add(item); } MyAdapter adapter = new MyAdapter( this , mInfos); mListView.setAdapter(adapter); setContentView(mListView); } private class MyAdapter extends BaseAdapter { private Context context; //运行上下文 private List<Map<String, Object>> listItems; //联系人信息集合 private LayoutInflater listContainer; //视图容器 private boolean[] hasChecked; //记录联系人选中状态 public final class ListItemView{ //自定义控件集合 public ImageView img; public TextView name; public TextView phone; public TextView region; public CheckBox check; public Button detail; } public MyAdapter(Context context, List<Map<String, Object>> listItems) { this .context = context; listContainer = LayoutInflater.from(context); //创建视图容器并设置上下文 this .listItems = listItems; hasChecked = new boolean[getCount()]; } public int getCount() { return listItems.size(); } public Object getItem(int position) { return null ; } public long getItemId(int position) { return 0; } public View getView(int position, View convertView, ViewGroup parent) { final int selectID = position; ListItemView listItemView = null ; if (convertView == null ) { listItemView = new ListItemView(); //获取list_item布局文件的视图 convertView = listContainer.inflate(R.layout.myinfo, null ); //获取控件对象 listItemView.img = (ImageView)convertView.findViewById(R.id.info_img); listItemView.name = (TextView)convertView.findViewById(R.id.info_name); listItemView.phone = (TextView)convertView.findViewById(R.id.info_phone); listItemView.region = (TextView)convertView.findViewById(R.id.info_region); listItemView.detail= (Button)convertView.findViewById(R.id.btn); listItemView.check = (CheckBox)convertView.findViewById(R.id.checkBox); //设置控件集到convertView convertView.setTag(listItemView); //设置联系人信息 listItemView.img.setBackgroundResource((Integer) listItems.get( position).get( "img" )); listItemView.name.setText((String) listItems.get( position).get( "name" )); listItemView.phone.setText((String) listItems.get( position).get( "phone" )); listItemView.region.setText((String) listItems.get( position).get( "region" )); //More按钮事件 listItemView.detail.setOnClickListener( new View.OnClickListener() { public void onClick(View v) { showDetailInfo(selectID); } }); // 注册多选框状态事件处理 listItemView.check .setOnCheckedChangeListener( new CheckBox.OnCheckedChangeListener() { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { //记录联系人选中状态 checkedChange(selectID); } }); } else { listItemView = (ListItemView)convertView.getTag(); } return convertView; } /** * 记录勾选了哪个联系人 * @param checkedID 选中的联系人序号 */ private void checkedChange(int checkedID) { hasChecked[checkedID] = !hasChecked(checkedID); } /** * 判断联系人是否选择 * @param checkedID 联系人序号 * @return 返回是否选中状态 */ public boolean hasChecked(int checkedID) { return hasChecked[checkedID]; } /** * 显示物品详情 * @param clickID */ private void showDetailInfo(int clickID) { new AlertDialog.Builder(context) .setIcon(Integer.parseInt(listItems.get(clickID).get( "img" ).toString())) .setTitle(listItems.get(clickID).get( "name" )+ "详细信息" ) .setMessage( "电话:" +listItems.get(clickID).get( "phone" ).toString()+ " 地区:" +listItems.get(clickID).get( "region" ).toString()) .setPositiveButton( "确定" , null ) .show(); } } } |
myinfo.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | <?xml version= "1.0" encoding= "utf-8" ?> <RelativeLayout xmlns:android= "http://schemas.android.com/apk/res/android" android:layout_width= "fill_parent" android:layout_height= "fill_parent" > <ImageView android:id= "@+id/info_img" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:layout_marginLeft= "3dp" /> <TextView android:id= "@+id/info_name" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:layout_toRightOf= "@id/info_img" android:layout_marginTop= "5dp" /> <TextView android:id= "@+id/info_phone" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:layout_below= "@id/info_name" android:layout_toRightOf= "@id/info_img" android:layout_alignBaseline= "@id/info_img" /> <TextView android:id= "@+id/info_region" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:layout_toRightOf= "@id/info_phone" android:layout_alignBaseline= "@id/info_phone" android:layout_marginLeft= "10dip" /> <CheckBox android:id= "@+id/checkBox" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:layout_marginLeft= "5dp" android:layout_alignBaseline= "@id/info_img" android:layout_toRightOf= "@id/info_region" /> <Button android:id= "@+id/btn" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:layout_alignBaseline= "@id/checkBox" android:layout_toRightOf= "@id/checkBox" android:text= "More..." /> </RelativeLayout> |
2、让Activity继承ListActivity构建ListView
如果程序的窗口仅仅需要显示一个列表,则可以让Activity直接继续ListActivity来实现。ListActivity的子类无需调用setContentView()方法来显示某个界面,而是可以直接传入一个Adapter,ListActivity的子类就可以呈现出一个列表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | package com.liuzhichao.listview; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import android.app.ListActivity; import android.os.Bundle; import android.widget.SimpleAdapter; public class ListActivityList extends ListActivity { //分别定义通讯录中的用户名、电话、地区等信息 private String[] info_Names={ "***" , "***" , "***" , "***" , "***" , "***" }; private String[] info_Phones={ "13844445144" , "13844444444" , "13444445144" , "13544445144" , "13644445144" , "13744445144" }; private String[] info_Regions={ "火星" , "水星" , "木星" , "月球" , "美国" , "未知地区" }; //定义一个ArrayList数组,每一条数据对应通讯录中的一个联系人信息 private ArrayList<Map<String,Object>> mInfos= new ArrayList<Map<String,Object>>(); @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super .onCreate(savedInstanceState); //添加联系人信息 for (int i=0;i<info_Names.length;i++){ Map<String,Object> item = new HashMap<String,Object>(); item.put( "img" , R.drawable.contact_img); item.put( "name" , info_Names[i]); item.put( "phone" , info_Phones[i]); item.put( "region" , info_Regions[i]); mInfos.add(item); } //定义一个SimpleAdapter SimpleAdapter adapter = new SimpleAdapter( this , mInfos, R.layout.info, new String[]{ "img" , "name" , "phone" , "region" }, new int[]{R.id.info_img,R.id.info_name,R.id.info_phone,R.id.info_region}); //本例中没有声明一个ListView,但通过继承ListActivity使用setListAdapter然后传入一个适配器即可直接显示一个列表。 setListAdapter(adapter); } } |
详解listview的四种适配器模式