最近做数独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; } }
最后问题圆满解决,运行效果