最近做数独Android程序,在选择数独题目时用到了ListActivity,ListActivity比较特别,在从一个Activity跳转到ListActivity时,结果报了异常,

上网查了下,原来虽然listactivity是Activity子类,但listactivity必须与布局文件(ListView)配合起来才能正常跳转,且ListView中android:id必须为@id/android:list。

 

使用了ListActivity类后,如果整个屏幕上只需显示一个列表,我们甚至可以把setContentView一行注释掉,不用定义列表的XML说明文件。因为ListActivity类已经默认绑定了一个ListView(列表视图)界面组件。
Sudoku_list.xml文件如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ListView android:id="@id/android:list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</LinearLayout>
如果使用自定义的list的话,我们还可以对每个list_item进行修改,设计出相应的界面。这时候需要定义sudoku_list_item.xmllayout文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="4sp">
<TextView
android:id="@+id/puzzle_id"
android:text="@string/puzzle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:textAppearance="?android:attr/textAppearanceMedium"/>
<TextView
android:id="@+id/rowId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toRightOf="@id/puzzle_id"
android:textAppearance="?android:attr/textAppearanceMedium"
/>

<TextView
android:id="@+id/state"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toRightOf="@id/rowId"
android:textAppearance="?android:attr/textAppearanceMedium"
/>

<TextView android:id="@+id/past_time"
android:text="@string/past_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/state"
android:layout_marginLeft="4sp"
android:layout_marginRight="8sp"
android:textAppearance="?android:attr/textAppearanceMedium"/>
"
<TextView
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/past_time"
android:layout_alignTop="@id/past_time"
android:layout_marginLeft="4sp"
android:layout_marginRight="8sp"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
<TextView
android:id="@+id/best_time"

android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_marginLeft="4sp"
android:layout_marginRight="8sp"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
<TextView android:id="@+id/best"
android:text="@string/best_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/best_time"
android:layout_marginLeft="4sp"
android:layout_marginRight="8sp"
android:textAppearance="?android:attr/textAppearanceMedium"/>

</RelativeLayout>
结果,一运行虽然能进去,但是每次运行后数据库里面的数据文件就会改变,我希望ListActivity的每个list_item也会随着改变,不幸的是list_item一直没有变化,只有当ListActivity重新onCreate()调用数据库时才能显示相应的改变。
上网查了下,有的说用handler启动一个新的线程对ListActivity进行实时更新,效果都不好。
最后我选择用SimpleCursorAdapter,完美解决实时更新问题,并且对于数据库很适合使用
public SimpleCursorAdapter (Context context, int layout, Cursor c, String[] from, int[] to)
第一个参数是当前Context提供了关于应用环境全局信息的接口,第二个参数是定义的list_item.xml文件,第三个参数是数据库的游标,如果还没有应用的话可以用null代替,第四个参数是数据库列表的每列的名称,第五个参数是对应的每个Item所要展示的TextView的id,并且只能是TextView,与第四个参数的对应的数据库里面的信息相对应。
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.sudoku_list);
//setListAdapter(new ArrayAdapter<String>(this,
// android.R.layout.simple_list_item_1, puz));
diff = getIntent().getIntExtra(Game.KEY_DIFFICULTY,
Game.DIFFICULTY_KIDS);
db = new DBAdapter(getApplicationContext());
adapter = new SimpleCursorAdapter(this, R.layout.sudoku_list_item,
null, new String[]{DBAdapter.KEY_ROWID, DBAdapter.KEY_IS_COMPLETE,
DBAdapter.KEY_COST_TIME, DBAdapter.KEY_BEST_TIME},
new int[]{R.id.rowId, R.id.state, R.id.time, R.id.best_time});
adapter.setViewBinder(new SudokuListViewBinder(this));
updateList();
setListAdapter(adapter);
}
updateList方法用于实时更新所有的list
private void updateList(){
if (mCursor != null) {
stopManagingCursor(mCursor);
}
mCursor = getSudokuList();
//updateInfo(mCursor);
startManagingCursor(mCursor);
adapter.changeCursor(mCursor);
}
最后一运行,虽然可以实时更新,但是因为SimpleCursorAdapter里面每个Item里面的TextView有一个是表示这个数独谜题所在的id,所以在程序里面显示的就是该谜题的id,因为第二个等级难度的数独题目的id是接在第一个等级数独题目的后面,也就是说,第二个等级的第一个题目本来应该显示Puzzle1,可是它显示的是Puzzle65(每个等级有64道题),SimpleCursorAdapter可以很好的解决ListActivity的实时更新问题,可是它显示的就是游标对应的数据不能更改,最后运行效果如下
相应的问题也有很多,比如数据库里面有个数据是isComplete,它是long型值,如果是0,就输出playing,如果是1,就输出solved。
最后参考了些开源的代码,找到解决办法了,使用了setViewBinder方法
public void setViewBinder (SimpleCursorAdapter.ViewBinder viewBinder)



Sets the binder used to bind data to views.



Parameters

viewBinder

the binder used to bind data to views, can be null to remove the existing binder

在程序里面定义了一个内部类,实现了ViewBinder接口,重写public boolean setViewValue(View view, Cursor c, int columnIndex)方法
private static class SudokuListViewBinder implements ViewBinder {
private Context mContext;

public SudokuListViewBinder(Context context) {
mContext = context;
}

@Override
public boolean setViewValue(View view, Cursor c, int columnIndex) {
TextView label = null;

switch (view.getId()) {
case R.id.rowId:
int id = c.getInt(c.getColumnIndex(DBAdapter.KEY_ROWID));
int puzzle = id - diff * NUM + 1;
label = (TextView) view;
label.setText(puzzle + "");
// TODO: still can be faster, I don't have to call initCollection and read notes

break;
case R.id.state:
label = ((TextView) view);
long state = c.getLong(c.getColumnIndex(DBAdapter.KEY_IS_COMPLETE));
label.setText(state == 1 ? " " + mContext.getString(R.string.Solved) :
" " + mContext.getString(R.string.playing));
break;
case R.id.time:
String cost_time = mGameTimeFormatter.format(
c.getLong(c.getColumnIndex(DBAdapter.KEY_COST_TIME)));
label = ((TextView) view);
if(cost_time.equals("00:00")){
label.setText(mContext.getString(R.string.default_time));
}else{
label.setText(cost_time);
}
break;
case R.id.best_time:
String bestTime = mGameTimeFormatter.format(
c.getLong(c.getColumnIndex(DBAdapter.KEY_BEST_TIME)));
label = ((TextView) view);
if(bestTime.equals("00:00")){
label.setText(mContext.getString(R.string.default_time));
}else{
label.setText(bestTime);
}

break;

}

return true;
}
}
最后问题圆满解决,运行效果


listView实时更新_4s