繼續上一個例子,結合ListView中對SQLite進行操作。
通過CursorAdapter在ListView中的資料呈現
在上一個例子中,我們可以對SQLite中的資料庫進行增刪改查,將資料讀到遊標Cursor中,然後一一讀出。在Android中可以通過CursorAdapter直接將資料對應到ListView中,如下處理:
public class Chapter22Test1 extends ListActivity{
private SQLiteDatabase db = null;
private Cursor cursor = null;
private SimpleCursorAdapter adapter = null;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
db= (new Chapter22Db (getApplicationContext())).getWritableDatabase();
cursor =db.rawQuery("SELECT _id,Name,Weight from mytable ORDER BY Weight", null);
//layout/chapter_22_test1.xml的Android XML檔案定義了ListView中每個單元的相片順序,每個單元R.id.c22_name和R.id.c22_gravity都是TextView,分列左右
adapter = new SimpleCursorAdapter(this,
R.layout.chapter_22_test1,
cursor,
new String[]{"Name","Weight"},//遊標資料的名稱,實際是Table列名字
new int[]{R.id.c22_name, R.id.c22_gravity});//對應的UI微件的id
setListAdapter(adapter);
}
protected void onDestroy() {
super.onDestroy();
cursor.close(); //我們在onCreate()中沒有關閉遊標,因為需要和ListView進行資料關聯,關閉curosr,會導致List無資料,故在最後釋放資源
db.close(); //斷開和資料庫的串連,釋放相關資源
}
}
更新資料(以增加為例)
我們要實現:通過Menu快顯功能表,有一個為增加,按之,彈出一個Dialog,可以在當中填入資料,按Dialog的確定按鍵,在SQLite資料庫的表格mytable中加入相關的資料,並且同步ListView的顯示。
第一步:建立OptionsMenu,裡面有菜單“Add”,按鍵後,觸發執行add()的操作。具體實現不在此羅嗦,可以參見Android學習筆記(八):Activity-OpenMenu和LinearLayout
第二步:在add()中,要完成彈出指定格式的Dialog,採用AlertDialog的方式,Dialog的格式在xml中給出。處理方式之前都學過,但是沒有合并使用的例子,包括Dialog的格式,同ListView中自訂元素的格式一樣,採用LayoutInflater。具體如下:
private void add(){
//步驟2.1:通過LayoutInflater從Android的XML檔案中產生View
LayoutInflater inflater = LayoutInflater.from(this);
final View addView = inflater.inflate(R.layout.add_dialgo,null);
//步驟2.2:通過AlertDialog彈出對話方塊,並且在第一個button,即PositiveButton監聽事件,觸發操作
new AlertDialog.Builder(this)
.setTitle("添加框")
.setView(addView)
.setPositiveButton("確定", new DialogInterface.OnClickListener() {
//我們希望得到addView中的資料,但是這個inner class,只能擷取final的值,所以之前將addView設定為final,也就是所有addView的地址是固定的,而不是動態產生。
public void onClick(DialogInterface dialog, int which) {
EditText nameView = (EditText)addView.findViewById(R.id.c22_name);
EditText weigthView = (EditText)addView.findViewById(R.id.c22_weight);
// addData是下面步驟三,實現SQLite的資料更新和ListView的顯示同步add(name,weight);
addData(nameView.getText().toString(), new Float(weigthView.getText().toString()).floatValue());
}
})
.setNegativeButton("取消",null)
.show();
}
第三步:更新資料庫和同步ListView,具體如下:
private void addData(String name ,float weight){
/* 略去資料的判斷,例如如果name一樣,採用update的方式等等*/
//步驟3.1 在資料庫表格中添加資料
ContentValues values = new ContentValues(2);
values.put("Name",name);
values.put("Weight",weight);
db.insert("mytable","Name",values);
//步驟3.2 同步ListView,更新遊標的資訊
cursor.requery();
}
非同步背景同步處理資料
在上面的例子,貌似可以,而且的確是可以,但是在Android的API文檔中,Cursor的方法requery()這樣寫道:This method is deprecated.Don't use this. Just request a new cursor, so you can do this asynchronously and update your list view once the new cursor
comes back. 這提示我們風險的存在,如果資料量大,會導致重寫讀取的事件長(也就是requery()的執行時間)。雖然手機是人手操作,互動頻率較低,在資料庫資料少的時候,例如上面的例子,我們仍然可以安全地使用requery。但是對於具有大量資料時,我們就需要修改上面的程式。
修訂的方式步驟如下:1,通過後台線程來讀取資料庫;2、通過更換cursor來更新ListView,具體如下:
//步驟1:通過後台線程AsyncTask來讀取資料庫,放入更換Cursor
private class RefreshList extends AsyncTask<Void, Void ,Cursor>{
//步驟1.1:在後台線程中從資料庫讀取,返回新的遊標newCursor
protected Cursor doInBackground(Void... params) {
Cursor newCursor = db.rawQuery("SELECT _id,Name,Weight from mytable ORDER BY Weight", null);
return newCursor;
}
//步驟1.2:線程最後執行步驟,更換adapter的遊標,並獎原遊標關閉,釋放資源
protected void onPostExecute(Cursor newCursor) {
adapter.changeCursor(newCursor);//網上看到很多問如何更新ListView的資訊,採用CusorApater其實很簡單,換cursor就可以
cursor.close();
cursor = newCursor;
}
}
//步驟2:取締requrey的方式,採用後台線程更新形式
private void addData(String name ,float weight){
... ...
//cursor.requery();
new RefreshList().execute();
}
通過ContextMenu來刪除ListView的資料
ContextMenu使用者手指長按某個View觸發的菜單,見Android 學習筆記(二七):Menu。這裡通過這個例子詳細展開。實現情境:使用者長按某個List元素,則彈出ContextMenu,選擇菜單“Delete”,按下後,彈出AlertDialog,請使用者再去確定是否刪除,確定後將資料從SQLite中刪除,並更新ListView的顯示。具體如下:
protected void onCreate(Bundle savedInstanceState) {
... ...
//步驟1:向ListView註冊Context Menu,當系統檢測到使用者長按某單元是,觸發Context Menu彈出
registerForContextMenu(getListView());
}
// 步驟2:建立ContextMenu同OptionMenu,使用者長按元素後,會快顯功能表
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
menu.add(Menu.NONE,DELETE_ID,Menu.NONE,"Delete");
super.onCreateContextMenu(menu, v, menuInfo);
}
//步驟 3: ContextMenu的觸發操作,例子將觸發delete()
public boolean onContextItemSelected(MenuItem item) {
switch(item.getItemId()){
case DELETE_ID:
/* 在此處,我們關鍵引入 AdapterView.AdapterContextMenuInfo來擷取單元的資訊。在有三個重要的資訊。 1、id:The row id of the item for which the context menu is being displayed ,在cursorAdaptor中,實際就是表格的_id序號; 2、position 是list的元素的順序;3、view就可以獲得list中點擊元素的View,通過view可以擷取裡面的顯示的資訊 */
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
delete(info.id);
return true;
default:
break;
}
return super.onContextItemSelected(item);
}
//步驟4: 對觸發彈框,和Add的相似,確定後,更新資料庫和更新ListView的顯示,上次學習已有相類的例子,不再重複。其中getNameById是通過id查名字的方法。值得注意的是,為了內部類中使用,delete的參數採用來final的形式。
private void delete(final long rowId){
if(rowId>0){
new AlertDialog.Builder(this)
.setTitle("刪除" + getNameById(rowId))
.setPositiveButton("確定", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
deleteData(rowId);
}
})
.setNegativeButton("取消", null)
.show();
}
}
private void deleteData(long rowId){
String[] str = {String.valueOf(rowId)};
db.delete("mytable","_id=?",str);
new RefreshList().execute(); //採用後台方式,當然也可以用crusor.requery()來處理。
}
通過模擬器的Console進行資料庫操作
通過android-sdk-linux_x86/platform-tools目錄下面有adb命令,使用adb shell,可提供模擬器的console視窗。資料庫檔案存放的位置為/data/data/your.app.package/databases/your-db-name,進入相關的目錄,可以使用#sqlite3
your-db-name,進入相關的資料庫,可以在裡面執行SQL語句,例如在整個例子中,通過#.schema來查看錶格的格式,通過#select * from mytable;可以顯示資料庫的內容。
相關連結:我的Andriod開發相關文章