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