ListActivity及ListView使用方法

概述

ListActivity是一个专门显示ListView的Activity类,可以用来显示一个列表数据,它内置了ListView对象,实现数据源的绑定与显示,数据源通常会是一个array或者一个拥有查询结果的cursor, 只要我们设置了数据源,ListView就会自动地显示出来。ListActivity的使用方法跟ListView基本一致, ListActivity提供多种显示样式,可以调用setListAdapter方法进行控制,如下图所示。


官方提供了多种ListItem的Layout (R.layout)用于控制显示样式,以下是较为常用的: 

Ø  android.R.layout.simple_list_item_1   一行text ;

Ø  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():

// setContentView(R.layout.activity_list_view);// 注释掉setContentView()

String[] pArray = getResources().getStringArray(R.array.drone_name);//获取strings.xml中定义的string-array

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

 

转载于:https://my.oschina.net/u/2509049/blog/638684

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值