ListActivity是一個專門顯示ListView的Activity類,能夠用來顯示一個列表數據,它內置了ListView對象,實現數據源的綁定與顯示,數據源一般會是一個array或者一個擁有查詢結果的cursor, 只要咱們設置了數據源,ListView就會自動地顯示出來。ListActivity的使用方法跟ListView基本一致, ListActivity提供多種顯示樣式,能夠調用setListAdapter方法進行控制,以下圖所示。 html
官方提供了多種ListItem的Layout (R.layout)用於控制顯示樣式,如下是較爲經常使用的: java
Ø android.R.layout.simple_list_item_1 一行text ; android
Ø android.R.layout.simple_list_item_2 一行title,一行text ; 數據庫
Ø android.R.layout.simple_list_item_single_choice 單選按鈕 數組
Ø android.R.layout.simple_list_item_multiple_choice 多選按鈕 網絡
Ø android.R.layout.simple_list_item_checked checkbox 多線程
ListActivity自己有一個默認的layout,其中包含一個全屏的listView。若是用默認的layout,必須將類定義爲ListActivity的子類:public class ListViewActivity extends ListActivity,並重寫onCreate()方法,每一個ListActivity系統都會給一個默認的窗口布局,所以在onCreate()中不須要setContentView(): app
// setContentView(R.layout.activity_list_view);// 註釋掉setContentView() 異步
String[] pArray = getResources().getStringArray(R.array.drone_name);//獲取strings.xml中定義的string-array ide
setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_ single_choice, pArray));//綁定數據到ListActivity的ListView,並設置顯示樣式;官方提供了多種ListItem的樣式;
雖然ListActivity內置ListView對象有默認的layout,但咱們依然可使用custom view,經過在onCreate()裏面調用setContentView(resources id)。不過要注意:在自定義的Layout裏面,必須包括一個(只能一個)ListView,並且要設置ListView對象的id爲"@android :id/list",不能隨便自定義id;在這個layout.xml文件中還能夠添加其餘的widget。在Java代碼裏使用android.R.id.list。 重寫onCreate()方法:
// use layout and widget defined in layout.xml
setContentView(R.layout.activity_list_view);
// get string-array defined in strings.xml
String[] pArray = getResources().getStringArray(R.array.drone_name);
setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_single_choice, pArray));
下面的例子,若是當ListView中沒有值而又想提示一句話時,那麼用於指定顯示提示信息的TextView的 id 必須爲 "@android :id/empty",當ListView裏面沒有data的時候,就會顯示。還能夠添加其餘的控件,如button等。
自定義的layout文件 (activity_list_view.xml):
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
tools:context="${relativePackage}.${activityClass}" >
<TextView
android:id="@+id/txtListTitle"
android:text="@string/title_activity_list_view"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="@android :color/holo_blue_light"
android:textSize="20sp"
android:layout_marginBottom="5dp"/>
<ListView
android:id="@id/android:list"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<TextView
android:id="@id/android:empty"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/nodata" />
<Button
android:id="@+id/btnback"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/action_settings" />
</LinearLayout>
在xml中添加了一個TextView顯示標題,一個button按鈕:
除了使用系統提供的幾種listItem樣式,還能夠自定義所須要的樣式,如同時顯示圖標和文本,自定義ListItem與自定義Layout的區別在於:前者定義的樣式當作ListView的一個Item;後者定義ListView替代ListActivity中定義的ListView,並能夠定義其餘的頁面元素。
ListItem示例(list_item_icon.xml)定義了一個Image控件用來顯示圖片和兩個文本(一個用來顯示標題,一個用來顯示詳細信息):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
tools:context="${relativePackage}.${activityClass}" >
<ImageView
android:id="@+id/imageView"
android:layout_width="86dp"
android:layout_height="86dp"
android:layout_centerVertical="true"
android:contentDescription="@string/description"
android:src="@drawable/ic_launcher" />
<TextView
android:id="@+id/titleText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toEndOf="@id/imageView"
android:layout_toRightOf="@id/imageView"
android:textSize="26sp"
android:textStyle="bold"
android:textColor="@android:color/holo_green_light"/>
<TextView
android:id="@+id/contentText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/titleText"
android:layout_toEndOf="@id/imageView"
android:layout_toRightOf="@id/imageView"
android:textSize="12sp" />
</RelativeLayout>
在Activity的onCreate()方法中使用ListActivity的默認layout,將註釋掉setContentView(R.layout.activity_list_view);而後建立一個Adapter,並綁定數據源,設置樣式。SimpleAdapter(Context context, List<? extends Map<String, ?>> data, @LayoutRes int resource, String[] from, @IdRes int[] to),參數resource設置爲上面定義的layout做爲item的樣式:R.layout.list_item_icon;詳細使用方法閱讀API文檔。
ListActivity中ListView是用來可視化數據的,其顯示樣式與綁定的數據源直接必須是匹配的。ListActivity定義了一個SetListAdapter(ListAdapter adapter)方法來實現數據綁定,參數ListAdapter爲一個接口,其繼承結構以下圖所示。androidAPI內置了幾個已經定義了幾個Adapter:ArrayAdapter,SimpleAdapter (以Map的形式存儲靜態數據),CursorAdapter (用於遊標查詢的結果)等。 數據源須要與顯示樣式配套,不然會出錯。
咱們也能夠implements ListAdapter來自定義數據源,一般咱們更多地extends BaseAdapter來編寫本身的Adapter類,由於BaseAdapter類是其餘Apdater類的基類。擴展BaseAdapter類通常都須要重寫如下方法:
Ø Int getCount() ;// 獲取當前Adapter的Items數目
Ø Object getItem(int position) ; // 獲取相應position的Item
Ø Long getItemId(int position); // 獲取相應position的Item在List中的row id
Ø View getView(int position, View convertView, ViewGroup parent); // 獲取在指定position所要顯示的data的View
詳細內容能夠查看BaseAdapter類的繼承android.widget.Adapter的方法,有時也須要重寫ListAdapter的boolean isEnabled(int position)來實現某些效果。
AndroidAPI已經定義的Atapter:
Ø ArrayAdapter<T>:
通常用來綁定一組對象到ListView,顯示在單個TextView對象中,如List<T>列表,T[]數組,會調用數組中每一個對象的toString()方法轉換爲String,而後顯示在TextView中,所以通常狀況須要重寫對象的toString()方法。ArrayAdapter<T>提供多個構造方法,經過指定一個LayoutRes中的資源ID,能夠用來實現更加複雜的顯示。
Ø CursorAdapter:
通常用來顯示從數據庫的查詢結果。Cursor必須包含一列名稱爲「_id」的字段。
Ø SimpleAdapter:
通常用來映射靜態數據到視圖對象中如ListView,可使用Map類型的List, List的每個條記錄對應一個Map對象,Map對象爲一個或多個key-value。能夠用來實現比較複雜的顯示樣式,如:圖表+文本+checkbox。
構造函數:SimpleAdapter(Context context, List<? extends Map<String, ?>> data, @LayoutRes int resource, String[] from, @IdRes int[] to),參數data爲map對象的List,每一個map對象能夠包含一個或多個key-value對,必須與參數String[]from對應;參數@LayoutRes int resource爲一個layout,能夠自定義layout.xml文件,layout中必須包含參數@IdRes int[] to中全部所需的資源ID,參數String[] from爲字段名稱,參數int[] to用來顯示全部的from參數。
Ø HeaderViewListAdapter:用來顯示帶標題的List。
爲達到不一樣的顯示效果,綁定數據的方法也不一樣:
// get string-array defined in strings.xml
String[] pArray = getResources().getStringArray(R.array.drone_name);
setListAdapter(new ArrayAdapter<String>(this, android.R.layout. simple_list_item_1, pArray));
ListView pListView = getListView();
pListView.setTextFilterEnabled(true);//設置過濾
String數組能夠換成其餘的Object對象數組,會調用對象的toString()方法轉換爲String類型顯示在TextView中,所以使用自定義對象的時候最好重寫其toString方法。顯示樣式也可使用其餘的item樣式,但不能設置爲:simple_list_item_2,simple_list_item_activated_2
列表的顯示樣式由參數ArrayAdapter 的實例化參數行控制,只須要將參數值由simple_list_item_1修改成:simple_list_item_single_choice,simple_list_item_multiple_choice便可。在java源代碼中須要調用方法setChoiceMode來控制是單選仍是多選。下面爲一個多選列表示例:
String[] pArray = getResources().getStringArray(R.array.drone_name);
setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_multiple_choice, pArray));
ListView pListView = getListView();
pListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
pListView.setTextFilterEnabled(true); //filter visual of items
pListView.setItemsCanFocus(true); // set whether items can get focus ,當有子控件嵌套時,設置爲true可讓子控件獲取焦點。
這種顯示樣式,可使用API中默認定義的item樣式:android.R.layout. simple_list_item_2,API中的simple_list_item_2.xml中定義了兩個text控件android.R.id.text1,android.R.id.text2用來分別顯示標題和詳細內容。綁定數據源使用SimpleAdapter,data採用map對象的List。
這種方式沒法實現豐富的UI,如字體大小、顏色等。要實現更加豐富的UI樣式,則須要自定義一個layout做爲item的樣式。
String[] pdroneName = getResources().getStringArray(R.array.drone_name);
String[] pdroneInfo = getResources().getStringArray(R.array.drone_info);
List<Map<String, String>> data = new ArrayList<Map<String, String>>();
if (pdroneInfo.length == pdroneName.length) {
for (int i = 0; i < pdroneName.length; i++) {
Map<String, String> drone = new HashMap<String, String>();
drone.put("dronename", pdroneName[i]);
drone.put("droneinfo", pdroneInfo[i]);
data.add(drone);
}
}
setListAdapter(new SimpleAdapter(this, data,
android.R.layout.simple_list_item_2,
new String[]{"dronename","droneinfo"},
new int[]{android.R.id.text1,android.R.id.text2}));
爲了實現帶圖標的列表,須要自定義一個layout.xml做爲item的樣式,layout.xml中需包含ImageView、Text等控件,綁定數據須要使用SimpleAdapter,定義一個map的list做爲數據源,map中的key與用於顯示其value的控件id創建對應關係。
使用ListActivity默認的ListView,所以不須要調用setContentView()方法。調用方法匹配數據源:setListAdapter(new SimpleAdapter(this, data, R.layout.list_item_icon, new String[] {"dronename", "droneinfo", "droneimage", "dronesel"}, new int[] { R.id.titleText, R.id.contentText, R.id.imageView , R.id.chkbox})),其中 dronename,droneinfo,droneimage爲map中對應的key,R.id.titleText,R.id.contentText,R.id.imageView爲layout中定義的控件id,map中的value將會顯示在對應的控件中。
一個應用程序被啓動時,系統默認建立執行一個叫作"main"的線程即UI線程。當處理的事件繁瑣,好比在響應用戶交互時需執行大量運算,或者像是執行網絡鏈接、數據庫請求這樣耗時的操做,就會形成擁堵,將會阻止整個界面的響應,通常超過3~5秒就會出現異常。好比:在主線程中綁定數據,若是數據源比較大,如上圖中的狀況,圖片文件比較大,可能會致使異常,提示:the application may be doing too much work in main thread。能夠採用多線程方式綁定數據源。
這種單線程的模式會帶來低性能,除非你能正確優化你的程序。對於單線程模式有兩個簡單的規則:
Ø 不要阻塞UI線程——不要在UI線程中執行長事務。
Ø 不要在非UI線程中操做界面。
Android提供了不少從其它線程來操做界面的方法,能夠用來綁定ListView的數據源:
Ø Activity.runOnUiThread(Runnable):不適合執行耗時的過程。在UI線程中執行Runnable中的run事務,若是當前線程是UI線程會當即執行run方法,若是當前線程爲非UI線程,會post到UI線程的消息隊列中。匿名對象方式調用:activity.runOnUiThread(new runnable(){});在runnable中的run方法中執行綁定數據源。
Ø View.post(Runnable):不適合執行耗時的過程。將runnable添加到消息隊列中,runnable執行在UI線程中。匿名對象方式調用:ListView.post(new runnable(){});在runnable中的run方法中執行綁定數據源。
Ø Handler+Thread:handler用來處理消息或者與線程消息隊列關聯的runnable對象,每個handler對象都與惟一線程(建立handler對象的那個線程)及其消息隊列關聯。Handle有兩種主要的用途:安排消息和runnable在合適的時間點執行;將一個action安排到另一個線程中執行。能夠經過handler來實現工做線程與UI線程之間的通訊,handler對象運行在UI線程中。通常使用過程爲:在UI線程中建立handler的子類對象,並在handleMessage方法中處理消息返回的數據,如綁定數據源到listView。建立thread的子類示例,並調用start方法。在thread的run方法中執行長事務或者在thread建立時候的runnable對象參數的run方法中執行。
Ø AsyncTask:輕量級的異步類,能夠直接繼承AsyncTask,在類中實現異步操做,並提供接口反饋當前異步執行的程度(能夠經過接口實現UI進度更新),最後反饋執行的結果給UI主線程。後臺方法doInBackground中執行長事務,後臺方法執行完後觸發onPostExecute,能夠用來綁定數據源。
示例一:
建立Handler的子類,並在handleMessage中adapter數據源到ListView。
//在ListActivity中建立一個內部類
private static class MyHandler extends Handler {
private ListActivity mListActivity;
//帶參數構造函數
public MyHandler(ListActivity pListActivity) {
// TODO Auto-generated constructor stub
this();
mListActivity = pListActivity;
}
private MyHandler() {
// TODO Auto-generated constructor stub
mListActivity = null;
}
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
if (mListActivity == null) {
return;
}
switch (msg.what) {
case MSG_SUCCESS:
SimpleAdapter pSimpleAdapter = (SimpleAdapter) msg.obj;
if (pSimpleAdapter == null) {
return;
}
mListActivity.setListAdapter(pSimpleAdapter);
break;
case MSG_FAILURE:
Log.i("FAIL", "get message failure");
break;
default:
break;
}
}
}
定義一個runnable子類,在run方法中完成SimpleAdapter對象建立,並將其做爲message的data發送給handler:
private class MyRunnable implements Runnable {
public void run() {
int mdroneImages[] = new int[] { R.drawable.dji, R.drawable.xplorer, R.drawable.ghost,
R.drawable.robotics_3d, R.drawable.jf, R.drawable.lily, R.drawable.ebee, R.drawable.intel,
R.drawable.hawa, R.drawable.xx };
List<Map<String, Object>> pList = new ArrayList<Map<String, Object>>();
Resources pResources = getResources();
String[] pdroneName = pResources.getStringArray(R.array.drone_name);
String[] pdroneInfo = pResources.getStringArray(R.array.drone_info);
if (pdroneInfo.length == pdroneName.length) {
for (int i = 0; i < pdroneName.length; i++) {
Map<String, Object> pMap = new HashMap<String, Object>();
pMap.put("dronename", pdroneName[i]);
pMap.put("droneinfo", pdroneInfo[i]);
if (i < mdroneImages.length) {
pMap.put("droneimage", mdroneImages[i]);
} else {
pMap.put("droneimage", R.drawable.dji);
}
pMap.put("dronesel", true);
pList.add(pMap);
}
}
SimpleAdapter pSimpleAdapter = new SimpleAdapter(ThreadHandlerListActivity.this, pList,
R.layout.list_item_icon, new String[] { "dronename", "droneinfo", "droneimage", "dronesel" },new int[] { R.id.titleText, R.id.contentText, R.id.imageView, R.id.chkbox });
mHandler.obtainMessage(MSG_SUCCESS,pSimpleAdapter).sendToTarget();
}
}
在ListActivity中的onCreate方法中建立handler、thread的實例,並調用start方法:
if (mHandler == null) {
mHandler = new MyHandler(this);
}
if (mRunnable == null) {
mRunnable = new MyRunnable();
}
if (mThread == null) {
mThread = new Thread(mRunnable);
mThread.start();
}
ListView中的條目click事件響應分爲:短按和長按事件,他們的處理方式是不一樣的。對於短按事件,處理起來比較簡單,咱們只須要重寫ListActivity的onListItemClick ()方法:
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
// TODO Auto-generated method stub
super.onListItemClick(l, v, position, id);
Toast.makeText(this, v.toString(), Toast.LENGTH_LONG).show();
Log.i("onListItemClick", "ListActivity onListItemClick");
}
另一種方法是爲使用setOnItemClickListener,爲ListView控件添加監聽。若是ListActivity中的ListView在註冊了一個單擊事件的監聽,則上文ListActivity的重寫方法onListItemClick不會被調用。
getListView().setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// TODO Auto-generated method stub
Toast.makeText(ListViewActivity.this, "ListView onItemClick", Toast.LENGTH_LONG).show();
Log.i("setOnItemClickListener", "ListView onItemClick");
}
});
長按事件監聽相似,調用setOnItemLongClickListener方法。
getListView().setOnItemLongClickListener(new OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
// TODO Auto-generated method stub
return false;
}
});
在處理ListView的事件響應時須要注意的是:Android ListActivity的Item中含有Button或者Checkable的子類,如ImageButton,EditText,checkbox和button等會自動獲得焦點的控件時,ListActivity的onListItemClick會不響應,能夠採用的解決辦法包括:
Ø 設置checkbox等控件讓其不自動得到焦點:將自定義layout中的checkbox等自動獲取焦點的控件android:focusable="false"。
Ø 設置控件獲取焦點的優先等級:使用自定義layout做爲ListView或ListActivity的ListItem時,能夠在自定義的layout根佈局中設置:android:descendantFocusability="blocksDescendants",這樣不須要對Item的每一個子控件設置是否獲取焦點。android有三種焦點傳遞方式: beforeDescendants(先於子控件獲取焦點), afterDescendants(只有全部子控件不須要焦點時才獲取焦點),blocksDescendants(阻止子控件獲取焦點,無論其是否須要)。
Ø 自定義adapter:能夠在bindView()函數中調用checkbox和button的setFocusable(false)和setFocusableInTouchMode(false);使它們失去焦點。
在一些特殊狀況下,Item採用自定義的UI,好比包含了ImageView,TextView,Button,Checkbox等。須要爲checkbox控件添加事件監聽的時候,則須要擴展Adapter對象。
網上有不少建議繼承BaseAdapter適合器進行處理,而後重寫BaseAdapter的getView方法。其實BaseAdapter是一個很是基礎的基類,對於通常的TextViwe ,ImageView,Button控件的數據綁定都沒有實現 ,若是咱們須要實現不是特別複雜的效果,則應該繼承SimpleAdapter進行重寫getView方法,在方法中經過findViewById獲取到須要添加事件監聽的控件,而後就能夠調用setOnCheckedChangeListener等方法添加事件監聽。在Activity中則可使用setListAdapter(new CustomerAdapter(……))能夠作到既實現Item事件的監聽,由實現Item中子控件事件的監聽。
須要注意的是,這種方式因爲在getView中直接使用了資源文件中的id,所以Adapter的可複用性不是很好,或者說在其餘的地方沒法直接複用。下面爲一個繼承SimpleAdapter類:
public class CustomerComplexAdapter extends SimpleAdapter {
public CustomerComplexAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from,
int[] to) {
super(context, data, resource, from, to);
// TODO Auto-generated constructor stub
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
View pView = super.getView(position, convertView, parent);
final CheckBox pCheckBox = (CheckBox) pView.findViewById(R.id.chkbox);
if (parent == null) {
Log.i("tag", "checkbox view is null");
return pView;
}
pCheckBox.setTag(position);
pCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// TODO Auto-generated method stub
String pString =Integer.toString(pCheckBox.getId()) + "===" +
Integer.toString(buttonView.getId()) + "++" + Boolean.toString(isChecked)+
"++"+buttonView.getTag().toString();
Log.i("tag",pString );
Toast.makeText(buttonView.getContext(), pString, Toast.LENGTH_LONG).show();
}
});
return pView;
}
延伸閱讀:
http://www.cnblogs.com/rocky_yi/archive/2011/03/14/ListActivity_setFocusable.html