首页 > 代码库 > 详解listview的四种适配器模式

详解listview的四种适配器模式

ListView是Android开发中比较常用的一个组件,它以列表的形式展示具体内容,并且能够根据数据的长度自适应显示。比如说我们手机里的通讯录就使用到了ListView显示联系人信息。ListView同时也是所有Android UI控件中最为麻烦的控件,之所以麻烦就是因为它的各种的适配器特别麻烦。

创建ListView有两种方式:

1、直接创建ListView

2、让Activity继承ListActivity

列表的显示需要三个元素:
1.ListVeiw :用来展示列表的View。
2.适配器 :用来把数据映射到ListView上的中介。
3.数据集   :具体的将被映射的字符串,图片,或者基本组件。

根据列表的适配器类型,列表分为四种,ArrayAdapterSimpleAdapterSimpleCursorAdapter以及自定义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 = newListView(this);
        //通过setAdapter构建一个ArrayAdapter将items与mListView"绑定"
        mListView.setAdapter(newArrayAdapter<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= newArrayList<Map<String,Object>>();
    //定义一个ListView
    private ListView mListView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        //new一个ListView
        mListView = newListView(this);
   
        //添加联系人信息
        for(int i=0;i<info_Names.length;i++){
             Map<String,Object> item = newHashMap<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 = newSimpleAdapter(this, mInfos, R.layout.info,
                newString[]{"img","name","phone","region"},
                newint[]{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中的数据。那么fromto呢?在加载列表项时,需要通过组件的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也类似。所以fromto中的参数是一一对应的关系。同时 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 = newListView(this);
           
         Cursor cursor=this.getContentResolver().query(Contacts.CONTENT_URI,null,null,null,null);  
         SimpleCursorAdapter adapter=newSimpleCursorAdapter(this, android.R.layout.simple_expandable_list_item_2, cursor,   
                    newString[]{Contacts.DISPLAY_NAME},
                    newint[]{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= newArrayList<Map<String,Object>>();
        //定义一个ListView
        private ListView mListView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        //new一个ListView
                mListView = newListView(this);
                   
                //添加联系人信息
                for(int i=0;i<info_Names.length;i++){
                     Map<String,Object> item = newHashMap<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 = newMyAdapter(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 = newboolean[getCount()];   
        }   
           
        public int getCount() {
            returnlistItems.size();
        }
   
        public Object getItem(int position) {
            returnnull;
        }
   
        public long getItemId(int position) {
            return0;
        }
   
        public View getView(int position, View convertView, ViewGroup parent) {
            final int selectID = position;
            ListItemView  listItemView = null;   
            if(convertView == null) {   
                listItemView = newListItemView();    
                //获取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(newView.OnClickListener() {
                    public void onClick(View v) {
                        showDetailInfo(selectID);
                    }
                });
                   
             // 注册多选框状态事件处理   
                listItemView.check   
                        .setOnCheckedChangeListener(newCheckBox.OnCheckedChangeListener() {   
                            public void onCheckedChanged(CompoundButton buttonView,   
                                    boolean isChecked) {   
                                //记录联系人选中状态   
                                checkedChange(selectID);   
                            }   
                });   
            }else{   
                listItemView = (ListItemView)convertView.getTag();   
            
            returnconvertView;
        }
           
        /**  
         * 记录勾选了哪个联系人 
         * @param checkedID 选中的联系人序号  
         */
        private void checkedChange(int checkedID) {   
            hasChecked[checkedID] = !hasChecked(checkedID);   
        }
           
        /**  
         * 判断联系人是否选择  
         * @param checkedID 联系人序号  
         * @return 返回是否选中状态  
         */ 
        public boolean hasChecked(int checkedID) {   
            returnhasChecked[checkedID];   
        
           
        /**  
         * 显示物品详情  
         * @param clickID  
         */ 
        private void showDetailInfo(int clickID) {   
            newAlertDialog.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= newArrayList<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 = newHashMap<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 = newSimpleAdapter(this, mInfos, R.layout.info,
                        newString[]{"img","name","phone","region"}, 
                        newint[]{R.id.info_img,R.id.info_name,R.id.info_phone,R.id.info_region});
                   
                //本例中没有声明一个ListView,但通过继承ListActivity使用setListAdapter然后传入一个适配器即可直接显示一个列表。
                setListAdapter(adapter);
    }
   
}

详解listview的四种适配器模式