ListView可謂是Android開發中使用非常頻繁的一個組件,在很多App中都能看到它的身影。Android游戲開發同樣少不了它,比較常見的如游戲關卡的選擇、游戲排行榜等,這些都可以用ListView來實現。
至於ListView的用法大家應該都清楚了,本文重點不是介紹ListView的使用,而是講解怎樣自定義一個通用適配器類。
在ListView三種適配器當中,最受大家青睐的肯定就是SimpleAdapter適配器,用過的童鞋們都很清楚,它的擴展性很強,可以將ListView中每一項都使用自定義布局,插入N多組件;但是SimpleAdapter也有弱點,那就是當ListView中每一項有Button、CheckBox等這些有事件的組件,我們想監聽它們就必須自定義適配器!那麼今天的重點也就是來講解一下如何寫一個自定義通用適配器類!
SimpleAdapter 構造的時候,我們知道需要五個參數來進行映射數據到ListView中,那麼我們今天的自定義通用適配器其實也就是實現系統SimpleAdapter的一個自定義版。
可能我說這麼多,大家還是不太懂,其實今天要講述的自定義通用適配器優點有三點:
1、使用通用適配器就不需要每次使用自定義適配器的時候,都要去重新去寫一個,太累。
2、構造方法與SimpleAdapter構造方法相同,五個參數也一摸一樣!
3、只需要在自定義的適配器類中,將我們需要監聽的組件進行設置監聽即可!別的代碼不需要去改動!
例如我們需要完成下圖這種ListView(圖1):
用SimpleAdapter實現界面
首先我們來完成ListView中每項的布局:
main.xml:
XML/HTML代碼
- <?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:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/iv"
- />
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- >
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="20sp"
- android:id="@+id/bigtv"
- />
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="10sp"
- android:id="@+id/smalltv"
- />
- </LinearLayout>
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="button"
- android:id="@+id/btn"
- />
- <CheckBox
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/cb"
- />
- </LinearLayout>
修改源碼:MainActivity.java:
Java代碼
- public class MainActivity extends Activity {
- private SimpleAdapter adapter;// 聲明適配器對象
- private ListView listView; // 聲明列表視圖對象
- private List<Map<String, Object>> list;// 聲明列表容器
- public static MainActivity ma;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- ma = this;
- // 實例化列表容器
- list = new ArrayList<Map<String, Object>>();
- listView = new ListView(this);// 實例化列表視圖
- // 實例一個列表數據容器
- Map<String, Object> map = new HashMap<String, Object>();
- // 往列表容器中添加數據
- map.put("item1_imageivew", R.drawable.icon);
- map.put("item1_bigtv", "BIGTV");
- map.put("item1_smalltv", "SMALLTV");
- // 將列表數據添加到列表容器中
- list.add(map);
- // --使用系統適配器,無法實現組件監聽;
- // //實例適配器
- adapter = new SimpleAdapter(this, list, R.layout.main, new String[] {
- "item1_imageivew", "item1_bigtv", "item1_smalltv" }, new int[] {
- R.id.iv, R.id.bigtv, R.id.smalltv });
- listView.setAdapter(adapter);
- // //顯示列表視圖
- this.setContentView(listView);
- }
- }
到此,我們就實現了圖1中的ListView,當然這裡只是完成了界面。
使用通用適配器
如果想監聽圖1中的按鈕和復選框事件,那麼我們肯定需要自定義一個適配器,那麼下面開始介紹如何實現通用適配器:
創建一個新類,類名:MySimpleAdapter.java,繼承自BaseAdapter:
Java代碼
- /**
- *
- */
- package com.himi;
- import java.util.List;
- import java.util.Map;
- import android.app.AlertDialog;
- import android.content.Context;
- 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.TextView;
- import android.widget.CompoundButton.OnCheckedChangeListener;
- /**
- * @author Himi
- *
- */
- public class MySimpleAdapter extends BaseAdapter {
- private LayoutInflater mInflater;
- private List<Map<String, Object>> list;
- private int layoutID;
- private String flag[];
- private int ItemIDs[];
- public MySimpleAdapter(Context context, List<Map<String, Object>> list,
- int layoutID, String flag[], int ItemIDs[]) {
- this.mInflater = LayoutInflater.from(context);
- this.list = list;
- this.layoutID = layoutID;
- this.flag = flag;
- this.ItemIDs = ItemIDs;
- }
- @Override
- public int getCount() {
- // TODO Auto-generated method stub
- return list.size();
- }
- @Override
- public Object getItem(int arg0) {
- // TODO Auto-generated method stub
- return 0;
- }
- @Override
- public long getItemId(int arg0) {
- // TODO Auto-generated method stub
- return 0;
- }
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- convertView = mInflater.inflate(layoutID, null);
- for (int i = 0; i < flag.length; i++) {//備注1
- if (convertView.findViewById(ItemIDs[i]) instanceof ImageView) {
- ImageView iv = (ImageView) convertView.findViewById(ItemIDs[i]);
- iv.setBackgroundResource((Integer) list.get(position).get(
- flag[i]));
- } else if (convertView.findViewById(ItemIDs[i]) instanceof TextView) {
- TextView tv = (TextView) convertView.findViewById(ItemIDs[i]);
- tv.setText((String) list.get(position).get(flag[i]));
- }else{
- //...備注2
- }
- }
- addListener(convertView);
- return convertView;
- }
- /**
- * 童鞋們只需要將需要設置監聽事件的組件寫在下面這方法裡就可以啦!
- * 別的不需要修改!
- * 備注3
- */
- public void addListener(View convertView) {
- ((Button)convertView.findViewById(R.id.btn)).setOnClickListener(
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- new AlertDialog.Builder(MainActivity.ma)
- .setTitle("自定義通用SimpleAdapter")
- .setMessage("按鈕成功觸發監聽事件!")
- .show();
- }
- });
- ((CheckBox)convertView.findViewById(R.id.cb)).
- setOnCheckedChangeListener(new OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- new AlertDialog.Builder(MainActivity.ma)
- .setTitle("自定義通用SimpleAdapter")
- .setMessage("CheckBox成功觸發狀態改變監聽事件!")
- .show();
- }
- });
- }
- }
備注1:這個For循環中是對ListView中每一項中包含所有的組件進行判定每個組件的類型,從而去設置其數據!
其中“instanceof”這個關鍵字可能有的童鞋不太熟習,這個是對Object 類型的判斷。
這裡我只是對ImageView、TextView的類型進行的數據識別,為什麼我這裡只寫了這兩種,那是因為Button、CheckBox等這些帶事件響應的組件是無法通過適配器映射到ListView上的。
關於適配器映射的機制,這裡簡單說下:例如一個TextView組件,那麼在ListView的每一項(List)中put()添加的時候,put()方法中第一個參數key大家知道是用於與適配器進行對應映射數據用的值,那麼第二個參數其實就是put進組件的數據;其實當其數據反射在ListViw時,其實內部就是對組件進行實例化,並且對組件設置數據。
備注2 :我這裡最後還有一個else{…}這裡是留給童鞋們去擴展的,因為可能還有一些其他能映射的組件,所以這裡留下接口,供大家擴展。
備注3:addListener(View convertView)這是我留出來的方法,童鞋們只需要將需要設置監聽事件的組件寫在這方法裡就可以啦!
那麼看一下我們使用通用監聽器的效果吧:
OK,很正常!那麼在來看看使用系統的SimpleAdapter 與我們自定義的MySimpleAdapter代碼對比圖:
怎麼樣?!構造參數完全一樣,而且我們這個比它強大,我們只要去設置下需要監聽的組件監聽代碼就OK了。
最後提醒大家:使用自定義適配器的時候,有時候ListView每一項的焦點沒有了,比如本文中是因為Button和CheckBox截獲了焦點,童鞋們只要將button和checkBox的焦點設置不可見就OK啦。
即xml中的focusable屬性: android:focusable=”false”
這裡也提醒一下開發游戲的童鞋們,很多游戲開發者認為開發游戲不用去學習系統組件的使用,不用去沾染xml、布局啥的,其實這麼想的童鞋們你們就大錯特錯了,Android之所以能這麼火,其組件的美觀占了很重的份量,這麼美的組件不用豈不是很浪費!!希望對組件不熟悉的游戲開發者去學習學習組件的使用!