ListActivity及ListView使用方法

概述

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  多線程

控制顯示樣式

(一)、使用默認layout

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的樣式;

(二)、使用自定義layout

雖然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樣式,還能夠自定義所須要的樣式,如同時顯示圖標和文本,自定義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_2simple_list_item_activated_2

(二) 單選列表/多選列表

列表的顯示樣式由參數ArrayAdapter 的實例化參數行控制,只須要將參數值由simple_list_item_1修改成:simple_list_item_single_choicesimple_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.titleTextR.id.contentTextR.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+Threadhandler用來處理消息或者與線程消息隊列關聯的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();

      }

添加事件響應

(一) ListItem添加事件響應

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);使它們失去焦點。

(二) ListItem子控件添加事件響應

在一些特殊狀況下,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://stackoverflow.com/questions/1821871/how-to-fire-onlistitemclick-in-listactivity-with-buttons-in-list

http://www.cnblogs.com/rocky_yi/archive/2011/03/14/ListActivity_setFocusable.html

http://my.oschina.net/u/1182603/blog/164201

相關文章
相關標籤/搜索