今天學習sqlite的時候,用到了simpleCursorAdapter.SimpleCursorAdapter是Android專門為了連接數據庫與視圖而產生的。他是將從數據庫表中獲取的數據顯示到ListView的橋梁。按照網上教程,我利用ListView體現SimpleCursorAdapter的用法。
1 ListView list=(ListView)findViewByID(R.id.listview); 2 SQLiteDatabase dbread=db.getReadableDatabase(); 3 Cursor cur=dbread.query("user",null,null,null,null,null,null); 4 adapter=new SimpleCursorAdapter(this,R.layout.layout,cur,new String[]{"name","sex"},new int[]{R.id.name,R.id.sex}); 5 listview.setAdapter(sca);
但是,調試的時候應用報錯,column '_id' does not exist。然后才看到教程里寫必須要求我們的數據庫中有一列,_id;SimpleCursorAdapter非常傲嬌, 如果沒有這列,就不干了。
還有兩個問題,1.SimpleCursorAdapter 中間出現了一道橫線,那說明這個函數已經過時,代替的函數是什么.2.當數據庫數據更新時,使用adapter.notifyDataSetChanged(),列表並未更新,那么如何保證UI及時更新?
找到的代替SimplecursorAdapter構造方法的函數是:SimpleCursorAdapter (Context context, int layout, Cursor c, String[] from, int[] to,int flags) 相較於之前的,僅僅多了一個 flags。flags在這里是用來標識當數據發生改變調用onContentChanged()的時候是否通知ContentProvider數據改變。對應有兩個常數:CursorAdapter。FLAG_AUTO_REQUERY和CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER。前者在api11后不推薦使用,就不在敘述。后者的作用是在 Cursor 上注冊一個內容監測器,並在其發出通知時調用 onContentChanged() 方法。
如果無需監聽ContentProvider的改變,或者,在CursorAdapter中使用了CursorLoader(他會為你注冊一個內容監測器),則可以傳0。
在UI更新方面,我找到三個方法:
cursor.requery();
adapter.notifyDataSetChanged();
adapter.swapCursor(newCursor);
adapter.notifyDataSetChanged();
adapter.changeCursor(newCursor);
adapter.notifyDataSetChanged();
第一種方法,requery被畫上了橫線,並且這個方法測試失敗了。
第二種方法和第三種方法都成功了。查閱資料,swapCursor和changeCursor兩種方法的區別介紹如下:
1 public Cursor swapCursor(Cursor newCursor) { 2 if (newCursor == mCursor) { 3 return null; 4 } 5 Cursor oldCursor = mCursor; 6 if (oldCursor != null) { 7 if (mChangeObserver != null) oldCursor.unregisterContentObserver(mChangeObserver); 8 if (mDataSetObserver != null) oldCursor.unregisterDataSetObserver(mDataSetObserver); 9 } 10 mCursor = newCursor; 11 if (newCursor != null) { 12 if (mChangeObserver != null) newCursor.registerContentObserver(mChangeObserver); 13 if (mDataSetObserver != null) newCursor.registerDataSetObserver(mDataSetObserver); 14 mRowIDColumn = newCursor.getColumnIndexOrThrow("_id"); 15 mDataValid = true; 16 // notify the observers about the new cursor 17 notifyDataSetChanged(); 18 } else { 19 mRowIDColumn = -1; 20 mDataValid = false; 21 // notify the observers about the lack of a data set 22 notifyDataSetInvalidated(); 23 } 24 return oldCursor; 25 }
swapCursor 交換了一個新的 Cursor,返回舊的Cursor,它並未將舊Cursor關閉
1 public void changeCursor(Cursor cursor) { 2 Cursor old = swapCursor(cursor); 3 if (old != null) { 4 old.close(); 5 } 6 }
changeCursor則替換將原來的 Cursor關閉了。
如果使用了CursorLoader(這真是一個好東西),它會管理Cursor,不需要我們自己關閉Cursor,loader會完成。我們只需實現下面三種方法即可
1 // Called when a new Loader needs to be created 2 public Loader<Cursor> onCreateLoader(int id, Bundle args) { 3 // Now create and return a CursorLoader that will take care of 4 // creating a Cursor for the data being displayed. 5 return new CursorLoader(this, ContactsContract.Data.CONTENT_URI, 6 PROJECTION, SELECTION, null, null); 7 } 8 9 // Called when a previously created loader has finished loading 10 public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 11 // Swap the new cursor in. (The framework will take care of closing the 12 // old cursor once we return.) 13 mAdapter.swapCursor(data); 14 } 15 16 // Called when a previously created loader is reset, making the data unavailable 17 public void onLoaderReset(Loader<Cursor> loader) { 18 // This is called when the last Cursor provided to onLoadFinished() 19 // above is about to be closed. We need to make sure we are no 20 // longer using it. 21 mAdapter.swapCursor(null);
參考資料:http://stackoverflow.com/questions/11093380/what-to-set-cursoradaptercontext-context-cursor-c-int-flags-to-in-order-to-m
http://www.blogc.at/2014/03/03/swapcursor-vs-changecursor-whats-the-difference/
http://developer.android.com/reference/android/widget/CursorAdapter.html#CursorAdapter(android.content.Context, android.database.Cursor, int)