ListView經過CursorAdapter顯示數據庫內容

繼續上一個例子,結合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做用是將layoutxml佈局文件實例化爲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);  

一直有點糾結setContentViewinflate的區別找了一些資料。寫了個小程序看了下:

?
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資源文件中
reSourceViewlayoutID
root 生成的層次結構的根視圖
return 填充的層次結構的根視圖。若是參數root提供了,那麼root就是根視圖;不然填充的XML文件的根就是根視圖。

其他幾個重載的inflate函數相似。

相關文章
相關標籤/搜索