本文可以帮助 完美解决 在Android中使用ListView时批量操作CheckBox出现的各种问题。
在Android中使用ListActivity可以很方便的绑定一组数据或者一个查询。但是,使用过程中也会遇到一些问题。在此,我将自己遇到的问题以及解决方法记录下来,一方面做一个备忘,同时,也希望有缘人能少走弯路。
问题一: Listview中的Item数目到底是多少
ListView中的Item数目可以使用getCount方法获得,经过验证得到的结果是,其Item数目等于界面上显示的Item数目,这个数目可能小于实际上绑定的数据条目数。
那么,在实际中如果有额外的非绑定数据源的数据需要编辑保存的时候,如何才能保存他们呢?
解决该问题的方法是:自定义ListAdapter,在其中保存额外需要保存的数据。
问题二:在Item中添加CheckBox出现麻烦了
在item中添加Checkbox的时候不小心会遇到麻烦,可能出现的情况是:
(1)Listview不能相应点击事件
(2)Listview点击第一个Item的时候最后一个Item也出现点击事件(反之亦然)
以上两种情况是我实际遇到的bug,经过各种纠结和反复测试,出现问题的原因是CheckBox相应焦点、点击事件的优先级别比Listview要高,所以出现问题。
解决方法如下(和问题一一对应):
(1)将Checkbox设置focusable属性为false
(2)接着将CheckBox设置Clickable属性为false.
以下是本人程序片段,仅供参考:
<!-- ans_list.xml -->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingLeft="8dp"
android:paddingRight="8dp" >
<TextView
android:id="@+id/ans_title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:gravity="center"
android:textSize="15sp"
android:textStyle="bold" />
<RelativeLayout
android:id="@+id/ans_foot"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:paddingLeft="8sp"
android:paddingRight="8sp" >
<CheckBox
android:id="@+id/ans_cbx_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginLeft="10sp"
android:checked="false" />
<Button
android:id="@+id/ans_btn_showInMap"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/ans_cbx_select"
android:text="@string/ans_btn_showInMap" />
</RelativeLayout>
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="@id/ans_foot"
android:layout_below="@id/ans_title"
android:drawSelectorOnTop="false" >
</ListView>
<TextView
android:id="@android:id/empty"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:text="@string/ans_list_empty"
android:textSize="25sp"
android:textStyle="bold" >
</TextView>
</RelativeLayout>
<!-- ans_item.xml -->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="10sp" >
<CheckBox
android:id="@+id/ans_item_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:focusable="false"
android:clickable="false"
android:checked="false"
android:gravity="center_vertical" />
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
>
....
<TextView
android:id="@+id/ans_item_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/ans_item_locname"
android:layout_toRightOf="@id/a4" />
</RelativeLayout>
</RelativeLayout>
public class AnsList extends ListActivity {
private PoiDbAdapter dba = null;
private Cursor cursor = null;
private ListView listView = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ans_list);
// 初始化标题
...
// 初始化列表
initList();
if (listView == null || listView.getCount() < 1) {
findViewById(R.id.ans_foot).setVisibility(View.GONE);
} else {
initEvents();
}
}
/** 初始化列表 */
private void initList() {
listView = getListView();
dba = new PoiDbAdapter(this);
cursor = dba.selectAnsByQnaire(QNaireBuilder.getQnaire(null)
.getQnaireId());
startManagingCursor(cursor);
// 构造游标适配器
MyCursorAdapter adapter = new MyCursorAdapter(this, R.layout.ans_item,
cursor, new String[] { ARemoteId, ALongi, ALati, ALocName,
ATime }, new int[] { R.id.ans_item_num,
R.id.ans_item_longi, R.id.ans_item_lati,
R.id.ans_item_locname, R.id.ans_item_time });
// 自定义ViewBinder,日期类型的数据需要转换
adapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
public boolean setViewValue(View view, Cursor cursor,
int columnIndex) {
String text = cursor.getString(columnIndex);
if (cursor.getColumnName(columnIndex).equalsIgnoreCase(ATime)) {
text = TimeTools.formatTime(Long.valueOf(text));
}
if (view instanceof TextView) {
((TextView) view).setText(text);
}
return true;
}
});
setListAdapter(adapter);
}
/** 为按钮和复选框添加事件处理, 为列表添加事件 */
private void initEvents() {
....
((CheckBox) findViewById(R.id.ans_cbx_select))
.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
selectAllOrNot(isChecked);
}
});
}
...
/** 全选或全部不选 */
protected void selectAllOrNot(boolean isChecked) {
int count = listView.getChildCount();
for (int i = 0; i < count; i++) {
((CheckBox) ((listView.getChildAt(i))
.findViewById(R.id.ans_item_select))).setChecked(isChecked);
}
((MyCursorAdapter) listView.getAdapter()).selectAll(isChecked);
}
...
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
CheckBox cbx = (CheckBox) v.findViewById(R.id.ans_item_select);
if (cbx != null) {
cbx.toggle();
((MyCursorAdapter) l.getAdapter())
.select(position, cbx.isChecked());
}
}
}
// 自定义ListAdapter
public class MyCursorAdapter extends SimpleCursorAdapter {
private HashMap<Integer, Boolean> isSelected = null;
public MyCursorAdapter(Context context, int layout, Cursor c,
String[] from, int[] to) {
super(context, layout, c, from, to);
isSelected = new HashMap<Integer, Boolean>();
selectAll(false);
}
// ListView加载新的Item的时候会调用这个方法
// 在加载前设置好意境保存好的Checkbox的状态
@Override
public View getView(int position, View convertView, ViewGroup parent) {
convertView = super.getView(position, convertView, parent);
Boolean b = isSelected.get(position);
if (null == b)
b = false;
if (convertView instanceof ViewGroup) {
ViewGroup g = (ViewGroup) convertView;
for (int i = 0; i < g.getChildCount(); i++) {
View v = g.getChildAt(i);
if (v instanceof CheckBox) {
((CheckBox) v).setChecked(b);
break;
}
}
}
return convertView;
}
public void select(int postion, boolean isChecked) {
isSelected.put(postion, isChecked);
}
public void selectAll(boolean isChecked) {
int a = this.getCount();
for (int b = 0; b < a; b++) {
select(b, isChecked);
}
}
}
经过折腾之后,终于顺利通过调试,可以轻松实现全选批量操作