繼續上一個例子,結合ListView中對SQLite進行操做。html
經過CursorAdapter在ListView中的數據呈現linux
在上一個例子中,咱們能夠對SQLite中的數據庫進行增刪改查,將數據讀到遊標Cursor中,而後一一讀出。在Android中能夠經過CursorAdapter直接將數據映射到ListView中,以下處理:android
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(); //斷開和數據庫的鏈接,釋放相關資源
}
}
sql
更新數據(以增長爲例)shell
咱們要實現:經過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();
}
app
第三步:更新數據庫和同步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,經過後臺線程來讀取數據庫;二、經過更換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來獲取單元的信息。在有三個重要的信息。 一、id:The row id of the item for which the context menu is being displayed ,在cursorAdaptor中,實際就是表格的_id序號; 二、position 是list的元素的順序;三、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;能夠顯示數據庫的內容。
LayoutInflater做用
LayoutInflater做用是將layout的xml佈局文件實例化爲View類對象。
獲取LayoutInflater的方法有以下三種:
LayoutInflater inflater=(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View layout = inflater.inflate(R.layout.main, null ); LayoutInflater inflater = LayoutInflater.from(context); (該方法實質就是第一種方法,可參考源代碼) View layout = inflater.inflate(R.layout.main, null ); LayoutInflater inflater = getLayoutInflater();(在Activity中可使用,其實是View子類下window的一個函數) View layout = inflater.inflate(R.layout.main, null ); |
一直有點糾結setContentView和inflate的區別找了一些資料。寫了個小程序看了下:
public class MyInflate extends Activity{ private TextView tv; public void OnCreate(Bundle savedInstanceState){ super .onCreate(savedInstanceState); //setContentView(R.layout.main); //tv = (TextView) findViewById(R.id.tv); LayoutInflater inflate = LayoutInflater.from( this ); View view = inflate.inflate(R.layout.main, null ); setContentView(view); } } |
上述註釋掉的代碼和沒有註釋掉的代碼兩種狀況是相同的。
區別:
setContentView()一旦調用, layout就會馬上顯示UI;而inflate只會把Layout造成一個以view類實現成的對象,有須要時再用setContentView(view)顯示出來。通常在activity中經過setContentView()將界面顯示出來,可是若是在非activity中如何對控件佈局設置操做了,這就須要LayoutInflater動態加載。
public View inflate(int Resourece,ViewGroup root)
做用:填充一個新的視圖層次結構從指定的XML資源文件中
reSource:View的layout的ID
root: 生成的層次結構的根視圖
return 填充的層次結構的根視圖。若是參數root提供了,那麼root就是根視圖;不然填充的XML文件的根就是根視圖。
其他幾個重載的inflate函數相似。