第二章 吸引你的眼球—UI編程(5),第二章ui
2.1.7列表組件(ListView)
ListView在Android中也是一個使用比較頻繁的組件。它相對於其他的基本組件來說,使用起來稍微複雜一些,需要注意的也比較多,尤其是和其他一些組件組合起來使用的情況。
在Android中,ListView用來顯示一個列表的組件,它以列表的形式展示具體的內容,並且能夠根據資料的長度自適應顯示。使用者可以選擇並操作這個列表,同時會觸發相應的事件:當滑鼠滾動時會觸發setOnItemSelectedListener事件;當點擊列表時會觸發setOnItemClickListener事件。
列表的顯示需要三個元素:
1) ListView:用來展示列表的View;
2) 適配器:用來把資料對應到ListView上的中介;
3) 資料:具體的將被映射的字串、圖片或者其他基本組件。
根據列表的適配器類型,列表分為三種:ArrayAdapter、SimpleAdapter和SimpleCursorAdapter。其中以ArrayAdapter最為簡單,只能展示一行字;SimpleAdapter有最好的擴充性,可以自訂出各種效果;SimpleCursorAdapter可以認為是SimpleAdapter對資料庫的簡單結合,可以方便地把資料庫中的內容以列表的形式展示出來。
下面,我們分別以一個例子來看看這些ListView是如何?的。
1)首先,我們來看看ArrayAdapter,部分代碼如下:
private ListView listView; @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); listView = new ListView(this); listView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_expandable_list_item_1,getData())); setContentView(listView); } private List<String> getData(){ List<String> data = new ArrayList<String>(); data.add("第一行"); data.add("第二行"); data.add("第三行"); return data; } |
讓我們來看看效果,2-15所示:
圖2-15 ArrayAdapter的使用
這種列表使用了ArrayAdapter(Context context,int textViewResourceId, List<T>objects)來裝配資料,ArrayAdapter的構造需要三個參數,依次為Context、布局檔案(注意這裡的布局檔案描述的是列表的每一行的布局,android.R.layout.simple_list_item_1是系統定義好的布局檔案只顯示一行文字,資料來源(一個List集合)。同時用setAdapter()完成適配。
2)SimpleCursorAdapter是把從遊標得到的資料進行列表顯示,並可以把指定的列映射到對應的TextView中。
下面的例子從電話簿中把連絡人顯示到類表中。先在通訊錄中添加一個連絡人作為資料庫的資料。然後獲得一個指向資料庫的Cursor並且定義一個布局檔案(當然也可以使用系統內建的)。
private ListView listView; @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); listView = new ListView(this); Cursor cursor = getContentResolver().query(People.CONTENT_URI, null, null, null, null); startManagingCursor(cursor); ListAdapter listAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_expandable_list_item_1, cursor, new String[]{People.NAME}, new int[]{android.R.id.name}); listView.setAdapter(listAdapter); setContentView(listView); } |
SimpleCursorAdapter 建構函式前面3個參數和ArrayAdapter是一樣的,最後兩個參數:一個包含資料庫的列的String型數組,一個包含布局檔案中對應組件id的int型數組。其作用是自動的將String型數組所表示的每一列資料對應到布局檔案對應id的組件上。上面的代碼,將NAME列的資料一次映射到布局檔案的id為name的組件上。
另外,讀取通訊錄需要在AndroidManifest.xml中如許可權:<uses-permissionandroid:name="android.permission.READ_CONTACTS"></uses-permission>。
效果2-16所示:
圖2-16 SimpleCursorAdapter的使用
3)SimpleAdapter是我們比較常用的一種列表。它的擴充性最好,可以定義各種各樣的布局出來,可以放上ImageView(圖片),還可以放上Button(按鈕),CheckBox(複選框)等等。我們也以一個例子來看看它是如何?的。
首先,我們定義一個list_item.xml檔案來定義list的內容:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/name_textview" android:layout_height="wrap_content" android:layout_width="100dp"/> <TextView android:id="@+id/phone_textview" android:layout_height="wrap_content" android:layout_width="150dp"/> <Button android:id="@+id/call_button" android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="呼叫"/> </LinearLayout> |
在這裡,我們定義兩個TextView、一個Button按鈕,接著我們建立一個MyList類繼承自ListActivity(ListActivity類繼承Activity類,預設綁定了一個ListView(列表視圖)介面組件,並提供一些與列表視圖、處理相關的操作),部分代碼如下:
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); SimpleAdapter adapter = new SimpleAdapter(this,getData(),R.layout.list_item, new String[]{"name","phone"}, new int[]{R.id.name_textview,R.id.phone_textview}); setListAdapter(adapter); } private List<Map<String, Object>> getData() { List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(); Map<String, Object> map = new HashMap<String, Object>(); map.put("name", "張三"); map.put("phone", "13472345623"); list.add(map); map = new HashMap<String, Object>(); map.put("name", "李四"); map.put("phone", "13472345623"); list.add(map); map = new HashMap<String, Object>(); map.put("name", "王五"); map.put("phone", "13472345623"); list.add(map); return list; } |
下面,我們來看看效果,2-17所示:
圖2-17 SimpleAdapter的使用
當我們點擊按鈕時,需要進行一系列的操作,但是上面那樣做無法滿足我們的需求。這是因為按鈕是無法映射的,即使我們成功的用布局檔案顯示出了按鈕也無法添加按鈕的響應,這樣的話就需要我們做一些別的事情來監聽按鈕事件:
首先,需要定義一個類ViewHolder,它裡面定義列表中的三個組件:
public final class ViewHolder{ public TextView name; public TextView phone; public Button call; } |
然後,我們定義一個mData用於儲存資料:
privateList<Map<String, Object>> mData;
|
接著,我們定義一個類MyAdapter繼承自BaseAdapter:
public class MyAdapter extends BaseAdapter{ private LayoutInflater mInflater; public MyAdapter(Context context){ this.mInflater = LayoutInflater.from(context); } @Override public int getCount() { return mData.size(); } @Override public Object getItem(int arg0) { return null; } @Override public long getItemId(int arg0) { return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { holder=new ViewHolder(); convertView = mInflater.inflate(R.layout.list_item, null); holder.name = (TextView)convertView.findViewById(R.id.name_textview); holder.phone = (TextView)convertView.findViewById(R.id.phone_textview); holder.call = (Button)convertView.findViewById(R.id.call_button); convertView.setTag(holder); }else { holder = (ViewHolder)convertView.getTag(); } holder.name.setText((String)mData.get(position).get("name")); holder.phone.setText((String)mData.get(position).get("phone")); holder.call.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.d(“test”,“call the number”); } }); return convertView; } } |
最後是onCreate方法:
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mData = getData(); MyAdapter adapter = new MyAdapter(this); setListAdapter(adapter); } |
這樣的話,我們就可以對列表中的每一個組件進行事件監聽了。如果我們要對ListView的每一行的點擊事件進行監聽的話,我們需要實現OnItemClickListener介面中的onItemClick方法:
@Override public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) { Log.d("test", "item: "+ arg2); } |
上面介紹的是使用ListActivity方式,因為它預設綁定了一個ListView,所以我們並不需要自己定義ListView就能直接使用了,但是很多時候,我們可能是在布局中自己定義一個ListView,這樣我們可以自己來控制它的大小等屬性,例如:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:id="@android:id/list" android:layout_width="fill_parent" android:layout_height="fill_parent"/> </LinearLayout> |
這裡需要注意的是,這個ListView的id必須為"@android:id/list",然後我們通過以下的代碼來實現:
AdapterView<?> mAdapterView = (ListView) findViewById(android.R.id.list); ((AbsListView) mAdapterView).setAdapter(adapter); |
如果用這種方法的話,我們除了可以實現OnItemClickListener介面之外,也可以直接對ListView綁定監聽事件來達到處理點擊列表每一行的效果:
mAdapterView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) { Log.d("test", "item: "+arg2); } }); |
經驗分享: 有的時候,我們為列表添加了OnItemClickListener監聽,但是點擊每一行後並沒有效果。這是因為,如果列表中有CheckBox或者是Button等存在的話,他們預設是獲得焦點的,而ListView的item能被選中的條件是要獲得焦點,因為我們在根控制項下設定如下屬性: android:descendantFocusability="blocksDescendants",這樣Item Layout就屏蔽了所有子控制項擷取Focus的許可權,而自己能夠獲得焦點響應點擊事件了。 |