Android開發:實現帶圖片和checkbox的listview
原文鏈接:http://mobile.51cto.com/android-254823.htm
標注:該listView中的checkbox實際上是不能獲取焦點的,點擊時監聽的是listView的OnItemClickListener方法.
先新建一個MyAdapter類,繼承自BaseAdapter。在MyAdapter中實現對數據的綁定,我這兒由於是測試的,所以把數據寫死了。
- package com.zwq.umeng;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import android.content.Context;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.BaseAdapter;
- import android.widget.CheckBox;
- import android.widget.ImageView;
- import android.widget.TextView;
- public class MyAdapter extends BaseAdapter {
- private LayoutInflater mInflater;
- private List<Map<String, Object>> mData;
- public static Map<Integer, Boolean> isSelected;
- public MyAdapter(Context context) {
- mInflater = LayoutInflater.from(context);
- init();
- }
- //初始化
- private void init() {
- mData=new ArrayList<Map<String, Object>>();
- for (int i = 0; i < 5; i++) {
- Map<String, Object> map = new HashMap<String, Object>();
- map.put("img", R.drawable.icon);
- map.put("title", "第" + (i + 1) + "行的標題");
- mData.add(map);
- }
- //這兒定義isSelected這個map是記錄每個listitem的狀態,初始狀態全部為false。
- isSelected = new HashMap<Integer, Boolean>();
- for (int i = 0; i < mData.size(); i++) {
- isSelected.put(i, false);
- }
- }
- @Override
- public int getCount() {
- return mData.size();
- }
- @Override
- public Object getItem(int position) {
- return null;
- }
- @Override
- public long getItemId(int position) {
- return 0;
- }
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- ViewHolder holder = null;
- //convertView為null的時候初始化convertView。
- if (convertView == null) {
- holder = new ViewHolder();
- convertView = mInflater.inflate(R.layout.vlist, null);
- holder.img = (ImageView) convertView.findViewById(R.id.img);
- holder.title = (TextView) convertView.findViewById(R.id.title);
- holder.cBox = (CheckBox) convertView.findViewById(R.id.cb);
- convertView.setTag(holder);
- } else {
- holder = (ViewHolder) convertView.getTag();
- }
- holder.img.setBackgroundResource((Integer) mData.get(position).get(
- "img"));
- holder.title.setText(mData.get(position).get("title").toString());
- holder.cBox.setChecked(isSelected.get(position));
- return convertView;
- }
- public final class ViewHolder {
- public ImageView img;
- public TextView title;
- public CheckBox cBox;
- }
- }
上面類中要注意getView()方法中對數據的處理。
接下要新建一個list.xml文件,這個就是布局image、textview、checkbox。
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <ImageView
- android:id="@+id/img"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"/>
- <TextView
- android:id="@+id/title"
- android:textSize="18dip"
- android:layout_weight="1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
- <CheckBox
- android:id="@+id/cb"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:focusable="false"
- android:focusableInTouchMode="false"
- android:clickable="false"
- android:checkMark="?android:attr/listChoiceIndicatorMultiple"/>
- </LinearLayout>
android:layout_weight="1"這一句可以使中間的textview權重增大,從而后面的checkbok可以居右顯示。
- android:focusable="false"
- android:focusableInTouchMode="false"
- android:clickable="false"
這三句很重要,如果不加就會出現錯誤。
由於checkbox的點擊事件優先級比listview的高,所以要在checkbox中添加android:focusable="false",使得checkbox初始的時候沒有獲取焦點。
接下來在main.xml中添加Listview組件
Java代碼
- <ListView
- android:id="@+id/lv"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"/>
接下來就是在activity中調用:
Java代碼
- list=(ListView)findViewById(R.id.lv);
- MyAdapter adapter=new MyAdapter(this);
- list.setAdapter(adapter);
- list.setItemsCanFocus(false);
- list.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
- list.setOnItemClickListener(new OnItemClickListener(){
- @Override
- public void onItemClick(AdapterView<?> parent, View view,
- int position, long id) {
- ViewHolder vHollder = (ViewHolder) view.getTag();
- //在每次獲取點擊的item時將對於的checkbox狀態改變,同時修改map的值。
- vHollder.cBox.toggle();
- MyAdapter.isSelected.put(position, vHollder.cBox.isChecked());
- }
- });
最新看一下效果圖:
要獲取哪些項目被選擇了,可以這樣測試:
Java代碼
- OnClickListener bPop = new OnClickListener() {
- @Override
- public void onClick(View v) {
- for(int i=0;i<list.getCount();i++){
- if(MyAdapter.isSelected.get(i)){
- ViewHolder vHollder = (ViewHolder) list.getChildAt(i).getTag();
- Log.i(TAG, "--onClick --"+vHollder.title.getText());
- }
- }
- }
- };
總結:
1、數據可以在Activity中獲取,在初始化Adapter的時候當做參數傳遞過去,這樣就可以是list編程動態的。
2、對MyAdapter中getview的理解不夠,尤其是convertView.setTag(holder),如果您對getview有什么新的使用心得可以發Email:zhousn@51cto.com,51CTO將與您一起分享!
Android開發:ListView詳解
原文鏈接:http://www.cnblogs.com/allin/archive/2010/05/11/1732200.html
標注:簡單介紹了三種adapter(適配器)實現的listView,以及在listView中的botton等控件如何獲得焦點
在android開發中ListView是比較常用的組件,它以列表的形式展示具體內容,並且能夠根據數據的長度自適應顯示。抽空把對ListView的使用做了整理,並寫了個小例子,如下圖。
列表的顯示需要三個元素:
1.ListVeiw 用來展示列表的View。
2.適配器 用來把數據映射到ListView上的中介。
3.數據 具體的將被映射的字符串,圖片,或者基本組件。
根據列表的適配器類型,列表分為三種,ArrayAdapter,SimpleAdapter和SimpleCursorAdapter
其中以ArrayAdapter最為簡單,只能展示一行字。SimpleAdapter有最好的擴充性,可以自定義出各種效果。SimpleCursorAdapter可以認為是SimpleAdapter對數據庫的簡單結合,可以方面的把數據庫的內容以列表的形式展示出來。
我們從最簡單的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
29
30
|
|
上面代碼使用了ArrayAdapter(Context context, int textViewResourceId, List<T> objects)來裝配數據,要裝配這些數據就需要一個連接ListView視圖對象和數組數據的適配器來兩者的適配工作,ArrayAdapter的構造需要三個參數,依次為this,布局文件(注意這里的布局文件描述的是列表的每一行的布局,android.R.layout.simple_list_item_1是系統定義好的布局文件只顯示一行文字,數據源(一個List集合)。同時用setAdapter()完成適配的最后工作。運行后的現實結構如下圖:
SimpleCursorAdapter
sdk的解釋是這樣的:An easy adapter to map columns from a cursor to TextViews or ImageViews defined in an XML file. You can specify which columns you want, which views you want to display the columns, and the XML file that defines the appearance of these views。簡單的說就是方便把從游標得到的數據進行列表顯示,並可以把指定的列映射到對應的TextView中。
下面的程序是從電話簿中把聯系人顯示到類表中。先在通訊錄中添加一個聯系人作為數據庫的數據。然后獲得一個指向數據庫的Cursor並且定義一個布局文件(當然也可以使用系統自帶的)。
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
|
|
Cursor cursor = getContentResolver().query(People.CONTENT_URI, null, null, null, null);先獲得一個指向系統通訊錄數據庫的Cursor對象獲得數據來源。
startManagingCursor(cursor);我們將獲得的Cursor對象交由Activity管理,這樣Cursor的生命周期和Activity便能夠自動同步,省去自己手動管理Cursor。
SimpleCursorAdapter 構造函數前面3個參數和ArrayAdapter是一樣的,最后兩個參數:一個包含數據庫的列的String型數組,一個包含布局文件中對應組件id的int型數組。其作用是自動的將String型數組所表示的每一列數據映射到布局文件對應id的組件上。上面的代碼,將NAME列的數據一次映射到布局文件的id為text1的組件上。
注意:需要在AndroidManifest.xml中如權限:<uses-permission android:name="android.permission.READ_CONTACTS"></uses-permission>
運行后效果如下圖:
SimpleAdapter
simpleAdapter的擴展性最好,可以定義各種各樣的布局出來,可以放上ImageView(圖片),還可以放上Button(按鈕),CheckBox(復選框)等等。下面的代碼都直接繼承了ListActivity,ListActivity和普通的Activity沒有太大的差別,不同就是對顯示ListView做了許多優化,方面顯示而已。
下面的程序是實現一個帶有圖片的類表。
首先需要定義好一個用來顯示每一個列內容的xml
vlist.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
|
|
下面是實現代碼:
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
|
|
使用simpleAdapter的數據用一般都是HashMap構成的List,list的每一節對應ListView的每一行。HashMap的每個鍵值數據映射到布局文件中對應id的組件上。因為系統沒有對應的布局文件可用,我們可以自己定義一個布局vlist.xml。下面做適配,new一個SimpleAdapter參數一次是:this,布局文件(vlist.xml),HashMap的 title 和 info,img。布局文件的組件id,title,info,img。布局文件的各組件分別映射到HashMap的各元素上,完成適配。
運行效果如下圖:
有按鈕的ListView
但是有時候,列表不光會用來做顯示用,我們同樣可以在在上面添加按鈕。添加按鈕首先要寫一個有按鈕的xml文件,然后自然會想到用上面的方法定義一個適配器,然后將數據映射到布局文件上。但是事實並非這樣,因為按鈕是無法映射的,即使你成功的用布局文件顯示出了按鈕也無法添加按鈕的響應,這時就要研究一下ListView是如何現實的了,而且必須要重寫一個類繼承BaseAdapter。下面的示例將顯示一個按鈕和一個圖片,兩行字如果單擊按鈕將刪除此按鈕的所在行。並告訴你ListView究竟是如何工作的。效果如下:
vlist2.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
|
|
程序代碼:
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
|
}
|
下面將對上述代碼,做詳細的解釋,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了。
運行效果如下圖:
你有多熟悉listView
原文鏈接:http://blog.csdn.net/xiedantibu/article/details/7050140
標注:listView的簡潔及其優化的方法
今天給大家帶來《android應用開發全程實錄》中關於listview和adatper中的部分。包括listview的基本使用,listview的優化等。
我們經常會在應用程序中使用列表的形式來展現一些內容,所以學好ListView是非常必需的。ListView也是Android中比較難以使用的控件,這節內容就將詳細解讀ListView的用法。
一個ListView通常有兩個職責。
(1)將數據填充到布局。
(2)處理用戶的選擇點擊等操作。
第一點很好理解,ListView就是實現這個功能的。第二點也不難做到,在后面的學習中讀者會發現,這非常簡單。
一個ListView的創建需要3個元素。
(1)ListView中的每一列的View。
(2)填入View的數據或者圖片等。
(3)連接數據與ListView的適配器。
也就是說,要使用ListView,首先要了解什么是適配器。適配器是一個連接數據和AdapterView(ListView就是一個典型的AdapterView,后面還會學習其他的)的橋梁,通過它能有效地實現數據與AdapterView的分離設置,使AdapterView與數據的綁定更加簡便,修改更加方便
Android中提供了很多的Adapter,表4-5列出了常用的幾個。
表4-5常用適配器
Adapter |
含義 |
ArrayAdapter<T> |
用來綁定一個數組,支持泛型操作 |
SimpleAdapter |
用來綁定在xml中定義的控件對應的數據 |
SimpleCursorAdapter |
用來綁定游標得到的數據 |
BaseAdapter |
通用的基礎適配器 |
其實適配器還有很多,要注意的是,各種Adapter只不過是轉換的方式和能力不一樣而已。下面就通過使用不同的Adapter來為ListView綁定數據(SimpleCursorAdapter暫且不講,后面講SQLite時會介紹)。
4.12.1 ListView使用ArrayAdapter
用ArrayAdapter可以實現簡單的ListView的數據綁定。默認情況下,ArrayAdapter綁定每個對象的toString值到layout中預先定義的TextView控件上。ArrayAdapter的使用非常簡單。
實例:
工程目錄:EX_04_12
在布局文件中加入一個ListView控件。
<?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent"> <!-- 添加一個ListView控件 --> <ListView android:id="@+id/lv" android:layout_width="fill_parent" android:layout_height="fill_parent"/> </LinearLayout>
然后在Activity中初始化。
publicclass MyListView extends Activity { privatestaticfinal String[] strs = new String[] { "first", "second", "third", "fourth", "fifth" };//定義一個String數組用來顯示ListView的內容private ListView lv; /** Called when the activity is first created. */ @Override publicvoid onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); lv = (ListView) findViewById(R.id.lv);//得到ListView對象的引用 /*為ListView設置Adapter來綁定數據*/ lv.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, strs)); } }
▲圖4-29 ListView使用ArrayAdapter運行效果
代碼非常的簡單,運行效果如圖4-29所示。
分析一下使用的步驟。
(1)定義一個數組來存放ListView中item的內容。
(2)通過實現ArrayAdapter的構造函數來創建一個ArrayAdapter的對象。
(3)通過ListView的setAdapter()方法綁定ArrayAdapter。
其中第二步有必要說一下的是,ArrayAdapter有多個構造函數,例子中實現的是最常用的一種。第一個參數為上下文,第二個參數為一個包含TextView,用來填充ListView的每一行的布局資源ID。第三個參數為ListView的內容。其中第二個參數可以自定義一個layout,但是這個layout必須要有TextView控件。通常我們使用Android提供的資源,除了例子中所用的,常用的還有如下幾種,可實現帶RadioButton和CheckBox的ListView。
(1)通過指定android.R.layout.simple_list_item_checked這個資源,實現帶選擇框的ListView。需要用setChoiceMode()方法設定選擇為多選還是單選,否則將不能實現選擇效果,運行效果如圖4-30所示。
實現代碼如下:
lv.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_checked, strs)); lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
(2)通過指定android.R.layout.simple_list_item_multiple_choice這個資源實現帶CheckBox的ListView。同樣的,需要用setChoiceMode()方法來設置單選或者多選,運行效果如圖4-31所示。
實現代碼如下:
lv.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_multiple_choice, strs)); lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
(3)通過指定android.R.layout.simple_list_item_single_choice這個資源實現帶RadioButton的ListView。這里要注意的是,這里並不是指定了單選。是多選還是單選要通過setChoiceMode()方法來指定,運行效果如圖4-32所示。
實現代碼如下:
lv.setAdapter(newArrayAdapter<String>(this,
android.R.layout.simple_list_item_single_choice,strs));
lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
▲圖4-30 帶選擇框的ListView
▲圖4-31 帶CheckBox的ListView
▲圖4-32 帶RadioButton的ListView
在前面講到過,ListView的職責除了填充數據外,還要處理用戶的操作。通過如下的代碼就可以為ListView綁定一個點擊監聽器,點擊后在標題欄顯示點擊的行數。
lv.setOnItemClickListener(new OnItemClickListener() { @Override publicvoid onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) { //點擊后在標題上顯示點擊了第幾行 setTitle("你點擊了第"+arg2+"行"); } });
4.12.2 ListView使用SimpleAdapter
很多時候需要在列表中展示一些除了文字以外的東西,比如圖片等。這時候可以使用SimpleAdapter。SimpleAdapter的使用也非常簡單,同時它的功能也非常強大。可以通過它自定義ListView中的item的內容,比如圖片、多選框等。看一個例子,實現一個每一行都有一個ImageView和TextView的ListView。先看一下運行效果,如圖4-34所示。
▲圖4-34 帶圖標的ListView
首先在布局文件中增加一個ListView控件。
還需要定義一個ListView中每一行的布局,用RelativeLayout來實現一個帶兩行字和一個圖片的布局。
item.xml:
<?xmlversion="1.0"encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="fill_parent" android:layout_width="fill_parent"> <ImageView android:layout_alignParentRight="true" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/ItemImage"/> <TextView android:id="@+id/ItemTitle" android:layout_height="wrap_content" android:layout_width="fill_parent" android:textSize="20sp"/> <TextView android:id="@+id/ItemText" android:layout_height="wrap_content" android:layout_width="fill_parent" android:layout_below="@+id/ItemTitle"/> </RelativeLayout>
配置完畢,就可以在Java代碼中為ListView綁定數據。
publicclass MyListViewSimple extends Activity { private ListView lv; /** Called when the activity is first created. */ @Override publicvoid onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); lv = (ListView) findViewById(R.id.lv); /*定義一個動態數組*/ ArrayList<HashMap<String, Object>> listItem = new ArrayList<HashMap<String, Object>>(); /*在數組中存放數據*/ for(int i=0;i<10;i++) { HashMap<String, Object> map = new HashMap<String, Object>(); map.put("ItemImage", R.drawable.icon);//加入圖片 map.put("ItemTitle", "第"+i+"行"); map.put("ItemText", "這是第"+i+"行"); listItem.add(map); } SimpleAdapter mSimpleAdapter = new SimpleAdapter(this,listItem,//需要綁定的數據 R.layout.item,//每一行的布局 //動態數組中的數據源的鍵對應到定義布局的View中new String[] {"ItemImage","ItemTitle", "ItemText"}, newint[] {R.id.ItemImage,R.id.ItemTitle,R.id.ItemText} ); lv.setAdapter(mSimpleAdapter);//為ListView綁定適配器 lv.setOnItemClickListener(new OnItemClickListener() { @Override publicvoid onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) { setTitle("你點擊了第"+arg2+"行");//設置標題欄顯示點擊的行 } }); } }
使用simpleAdapter的數據一般都是用HashMap構成的列表,列表的每一節對應ListView的每一行。通過SimpleAdapter的構造函數,將HashMap的每個鍵的數據映射到布局文件中對應控件上。這個布局文件一般根據自己的需要來自己定義。梳理一下使用SimpleAdapter的步驟。
(1)根據需要定義ListView每行所實現的布局。
(2)定義一個HashMap構成的列表,將數據以鍵值對的方式存放在里面。
(3)構造SimpleAdapter對象。
(4)將LsitView綁定到SimpleAdapter上。
4.12.3 ListView使用BaseAdapter與ListView的優化
在ListView的使用中,有時候還需要在里面加入按鈕等控件,實現單獨的操作。也就是說,這個ListView不再只是展示數據,也不僅僅是這一行要來處理用戶的操作,而是里面的控件要獲得用戶的焦點。讀者可以試試用SimpleAdapter添加一個按鈕到ListView的條目中,會發現可以添加,但是卻無法獲得焦點,點擊操作被ListView的Item所覆蓋。這時候最方便的方法就是使用靈活的適配器BaseAdapter了。
▲圖4-35 BaseAdapter中的方法
使用BaseAdapter必須寫一個類繼承它,同時BaseAdapter是一個抽象類,繼承它必須實現它的方法。BaseAdapter的靈活性就在於它要重寫很多方法,看一下有哪些方法,如圖4-35所示為繼承自BaseAdapter的SpeechListAdapter所實現的方法,其中最重要的即為getView()方法。這些方法都有什么作用呢?我們通過分析ListView的原理來為讀者解答。
當系統開始繪制ListView的時候,首先調用getCount()方法。得到它的返回值,即ListView的長度。然后系統調用getView()方法,根據這個長度逐一繪制ListView的每一行。也就是說,如果讓getCount()返回1,那么只顯示一行。而getItem()和getItemId()則在需要處理和取得Adapter中的數據時調用。那么getView如何使用呢?如果有10000行數據,就繪制10000次?這肯定會極大的消耗資源,導致ListView滑動非常的慢,那應該怎么做呢?通過一個例子來講解如何在使用BaseAdapter的時候優化ListView的顯示。例子中將上一節中的ImageView換成Button,並且處理Button的點擊事件,其中對ListView的顯示做了優化。
布局文件和上一例類同,讀者可以在光盤的工程目錄中查看,這里只給出Activity類。
publicclass MyListViewBase extends Activity { private ListView lv; /*定義一個動態數組*/ ArrayList<HashMap<String, Object>>listItem; /** Called when the activity is first created. */ @Override publicvoid onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); lv = (ListView) findViewById(R.id.lv); MyAdapter mAdapter = new MyAdapter(this);//得到一個MyAdapter對象lv.setAdapter(mAdapter);//為ListView綁定Adapter /*為ListView添加點擊事件*/ lv.setOnItemClickListener(new OnItemClickListener() { @Override publicvoid onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) { Log.v("MyListViewBase", "你點擊了ListView條目" + arg2);//在LogCat中輸出信息 } }); } /*添加一個得到數據的方法,方便使用*/ private ArrayList<HashMap<String, Object>> getDate(){ ArrayList<HashMap<String, Object>> listItem = new ArrayList<HashMap<String, Object>>(); /*為動態數組添加數據*/ for(int i=0;i<30;i++) { HashMap<String, Object> map = new HashMap<String, Object>(); map.put("ItemTitle", "第"+i+"行"); map.put("ItemText", "這是第"+i+"行"); listItem.add(map); } return listItem; } /* * 新建一個類繼承BaseAdapter,實現視圖與數據的綁定 */ privateclass MyAdapter extends BaseAdapter { private LayoutInflater mInflater;//得到一個LayoutInfalter對象用來導入布局 /*構造函數*/ public MyAdapter(Context context) { this.mInflater = LayoutInflater.from(context); } @Override publicint getCount() { return getDate().size();//返回數組的長度 } @Override public Object getItem(int position) { returnnull; } @Override publiclong getItemId(int position) { return 0; } /*書中詳細解釋該方法*/ @Override public View getView(finalint position, View convertView, ViewGroup parent) { ViewHolder holder; //觀察convertView隨ListView滾動情況 Log.v("MyListViewBase", "getView " + position + " " + convertView); if (convertView == null) { convertView = mInflater.inflate(R.layout.item, null); holder = new ViewHolder(); /*得到各個控件的對象*/ holder.title = (TextView) convertView.findViewById(R.id.ItemTitle); holder.text = (TextView) convertView.findViewById(R.id.ItemText); holder.bt = (Button) convertView.findViewById(R.id.ItemButton); convertView.setTag(holder);//綁定ViewHolder對象 } else{ holder = (ViewHolder)convertView.getTag();//取出ViewHolder對象 } /*設置TextView顯示的內容,即我們存放在動態數組中的數據*/ holder.title.setText(getDate().get(position).get("ItemTitle").toString()); holder.text.setText(getDate().get(position).get("ItemText").toString()); /*為Button添加點擊事件*/ holder.bt.setOnClickListener(new OnClickListener() { @Override publicvoid onClick(View v) { Log.v("MyListViewBase", "你點擊了按鈕" + position); //打印Button的點擊信息 } }); return convertView; } } /*存放控件*/ publicfinalclass ViewHolder{ public TextView title; public TextView text; public Button bt; } }
運行效果如圖4-36所示。還需要注意的是,Button會搶奪ListView的焦點,需要將Button設置為沒有焦點。設置非常簡單,只需要在xml的Button標簽下加入一行:android:focusable=“false”代碼就可以了。在LogCat觀察點擊后輸出的信息,如圖4-37所示。
▲圖4-36 使用BaseAdapter的ListVie
w
▲圖4-37 點擊ListView條目和Button得到的輸出
代碼中getView()方法不容易理解。其實完全可以不用所謂的convertView和ViewHolder,直接導入布局並且設置控件顯示的內容就可以了。但是這意味着有多少行數據就需要繪制多少行ListView,這顯然是不可取的。這里采用了一種優化的方法。代碼中,在getView()方法中加入了一行log輸出convertView的內容。滾動ListView,輸出信息如圖4-38所示。
從圖4-38中可以看出,當啟動Activity呈現第一屏ListView的時候,convertView為零。當用戶向下滾動ListView時,上面的條目變為不可見,下面出現新的條目。這時候convertView不再為空,而是創建了一系列的convertView的值。當又往下滾一屏的時候,發現第11行的容器用來容納第22行,第12行的容器用來容納第23行。也就是說convertView相當於一個緩存,開始為0,當有條目變為不可見,它緩存了它的數據,后面再出來的條目只需要更新數據就可以了,這樣大大節省了系統資料的開銷。
還可以繼續優化。雖然重復利用了已經繪制的view,但是要得到其中的控件,需要在控件的容器中通過findViewById的方法來獲得。如果這個容器非常復雜,這顯然會增加系統資源的開銷。在上面的例子中,引入了Tag的概念。或許不是最好的辦法,但是它確實能使ListView變得更流暢。代碼中,當convertView為空時,用setTag()方法為每個View綁定一個存放控件的ViewHolder對象。當convertView不為空,重復利用已經創建的view的時候,使用getTag()方法獲取綁定的ViewHolder對象,這樣就避免了findViewById對控件的層層查詢,而是快速定位到控件。
▲圖4-38 滾動ListView輸出的convertView的值
總結一下,這節介紹了用BaseAdapter來綁定ListView的數據。因為BaseAdapter非常靈活,使用也相對較其他控件麻煩。同時ListView的優化問題也值得讀者去研究,一個流暢的ListView會帶來更好的用戶體驗。