1. 程式人生 > >Android應用開發-資料儲存和介面展現(二)

Android應用開發-資料儲存和介面展現(二)

SQLite資料庫

// 自定義類MyOpenHelper繼承自SQLiteOpenHelper
MyOpenHelper oh = new MyOpenHelper(getContext(), "school.db", null, 1);
// 獲取資料庫物件,如果資料庫不存在,會自動建立資料庫,資料庫檔名為school.db,資料庫版本為1
SQLiteDatabase db = oh.getWritableDatabase();

  getWritableDatabase():以讀寫的方式開啟資料庫對應的SQLiteDatabase物件,磁碟空間不足時會報錯

  getReadableDatabase():該函式首先呼叫getWritableDatabase(),如果磁碟空間不足,會返回開啟失敗,然後以只讀的方式開啟資料庫對應的SQLiteDatabase物件

  MyOpenHelper的程式碼如下:

public class MyOpenHelper extends SQLiteOpenHelper
{

    public MyOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version)
    {
        super(context, name, factory, version);
    }
    
    @Override
    public void onCreate(SQLiteDatabase db)     //
資料庫建立時,此方法會呼叫 { db.execSQL("create table teacher(_id integer primary key autoincrement, name char(10), salary char(20), phone integer(20))"); // 一般以_id作為主鍵 } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) // 資料庫升級時,此方法會呼叫 { System.out.println(
"資料庫升級了"); } }

資料庫的增刪改查

SQL語句

  insert into teacher (name, phone, money) values ('張三', '159874611', 2000);

  delete from teacher where name = '李四' and _id = 4;

  update teacher set money = 6000 where name = '李四';

  select name, phone from teacher where name = '張三';

執行SQL語句實現增刪改查

db.execSQL("insert into teacher (name, phone, money) values (?, ?, ?);", new Object[]{"張三", 15987461, 75000});// 插入
Cursor cs = db.rawQuery("select _id, name, money from teacher where name = ?;", new String[]{"張三"});        // 查詢

使用API實現增刪改查

  插入

// 把要插入的資料以鍵值對的形式封裝至ContentValues物件
ContentValues cv = new ContentValues();
cv.put("name", "劉能");
cv.put("phone", 1651646);
cv.put("money", 3500);
long i = db.insert("teacher", null, cv);  // 返回值是所插入行的主鍵,如果出錯返回-1

  刪除

int i = db.delete("teacher", "_id = ? and name = ?", new String[]{"1", "張三"});    // 返回值是刪除的行數

  修改

ContentValues cv = new ContentValues();
cv.put("money", 25000);
int i = db.update("teacher", cv, "name = ?", new String[]{"趙四"});    // 返回值是所修改行的主鍵,如果出錯返回-1

  查詢

// arg2:要查詢的欄位
// arg3:查詢條件
// arg4:填充查詢條件的佔位符
Cursor cs = db.query("teacher", new String[]{"name", "money"}, "name = ?", new String[]{"張三"}, null, null, null);
while(cs.moveToNext()){
    // 獲取指定列的索引值
    String name = cs.getString(cs.getColumnIndex("name"));
    String money = cs.getString(cs.getColumnIndex("money"));
    System.out.println(name + ";" + money);
}

事務

  保證多條SQL語句要麼同時成功,要麼同時失敗

  最常見案例:銀行轉賬

  事務API

try {
    // 開啟事務
    db.beginTransaction();
    ...........
    // 設定事務執行成功
    db.setTransactionSuccessful();
} finally{
    // 關閉事務
    // 如果此時已經設定事務執行成功,則sql語句生效,否則不生效
    db.endTransaction();
}

把資料庫的資料顯示至螢幕

  1. 任意插入一些資料

  2. 定義業務bean:Teacher.java

  3. 讀取資料庫的所有資料

Cursor cs = db.query("teacher", null, null, null, null, null, null);
while(cs.moveToNext()){
    String name = cs.getString(cs.getColumnIndex("name"));
    String phone = cs.getString(cs.getColumnIndex("phone"));
    String money = cs.getString(cs.getColumnIndex("money"));
    Teacher t = new Teacher(name, phone, money);// 把讀到的資料封裝至Teacher物件
    list.add(t);    // 把Teacher物件儲存至集合中
}

  4. 把集合中的資料顯示至螢幕

LinearLayout ll = (LinearLayout) findViewById(R.id.ll);
 for(Teacher t : list){
     TextView tv = new TextView(this); // 建立TextView,每條資料用一個文字框顯示
     tv.setText(t.toString());
     ll.addView(tv);    // 把文字框設定為ll的子節點
 }

  分頁查詢

Cursor cs = db.query("teacher", null, null, null, null, null, null, "0, 10");

ListView的使用及優化

  LsitView用來顯示一行一行的條目,每一個條目都是一個View物件,可以由佈局檔案填充(inflate)而來

  MVC架構

    M:model模型層,提供要展示的資料 ———— List集合

    V:view檢視層,使用者看到的介面 ———— ListView

    C:control控制層,操作資料如何顯示 ———— Adapter

擴充套件BaseAdapter

  擴充套件BaseAdapter實現我們自己的Adapter可以對列表項進行最大限度的定製,它需要實現四個方法,其中最主要的是下面這兩個方法:

    第一個

// 系統呼叫此方法,用來獲知模型層有多少條資料
public int getCount() {
    return list.size();
}

    第二個

// 由系統呼叫,獲取一個View物件,作為ListView的條目
// position:本次getView方法呼叫所返回的View物件,在ListView中位於第幾個條目,那麼position的值就是多少
@Override
public View getView(int position, View convertView, ViewGroup parent){
    
    View v = View.inflate(MainActivity.this, R.layout.item_listview, null);    // 則把佈局檔案填充成一個View物件
    // 通過資源id查詢控制元件,注意呼叫的是View物件的findViewById 
    TextView tv_name = (TextView) v.findViewById(R.id.tv_name);
    TextView tv_phone = (TextView) v.findViewById(R.id.tv_phone);
    TextView tv_salary = (TextView) v.findViewById(R.id.tv_salary);
Teacher t
= list.get(position); tv_name.setText(t.getName()); tv_phone.setText(t.getPhone()); tv_salary.setText(t.getSalary()); return v; }

  一旦給ListView設定了Adapter,即 listView.setAdapter(new MyAdapter()); ,上面的方法就開始呼叫,螢幕上顯示多少個條目,getView方法就會被呼叫多少次,當向下滑動螢幕時,getView會繼續被呼叫,獲得更多的View物件顯示至ListView

使用條目的快取convertView

    當ListView中的某條目(View)徹底劃出螢幕時,系統會把該條目快取至記憶體(記憶體充足時),這時記憶體中就有了條目快取。如果此時使用者繼續滑動到新的條目,系統在呼叫getView時會把記憶體中已快取的條目取出並作為convertView引數傳入,但是傳入的條目不一定是我們需要的那個條目,即系統有可能正在呼叫getView方法獲取第10個條目,convertView傳入的卻是第1個條目的快取,所以即使在條目快取存在( convertView != null )時,我們仍需要對條目中的內容重新賦值,以達到複用快取條目(即複用View,避免了不必要的inflate操作。另外,因為inflate操作耗時較長,所以Android系統使用非同步操作來實現它)的目的,避免產生OOM(Out Of Memory)。

    對上面的getView()進行改造,使用convertView複用快取條目的程式碼如下:

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    if (convertView == null) {    // 沒有快取,則把佈局檔案填充成一個View物件
        convertView = View.inflate(MainActivity.this, R.layout.item_listview, null);
    }
    TextView tv_name = (TextView) convertView.findViewById(R.id.tv_name);
    TextView tv_phone = (TextView) convertView.findViewById(R.id.tv_phone);
    TextView tv_salary = (TextView) convertView.findViewById(R.id.tv_salary);

    Teacher t = list.get(position);
    tv_name.setText(t.getName());
    tv_phone.setText(t.getPhone());
    tv_salary.setText(t.getSalary());
    return convertView;
}

使用ViewHolder模式提高效率

  findViewById()是通過在控制元件樹中以深度優先遍歷來查詢與指定資源id匹配的控制元件的,上面程式碼儘管複用了快取條目convertView,但是在使用者反覆滑動ListView的過程中仍會大量的呼叫findViewById(),從而耗費大量的記憶體資源,我們可以使用ViewHolder模式對上面的程式碼進行進一步的優化。使用ViewHolder模式來優化ListView非常簡單,只需要在自定義Adapter中定義一個內部類ViewHolder,並將佈局中的控制元件作為ViewHolder的成員變數

final class ViewHolder {  // ViewHolder優化:防止大量的findViewById操作耗費資源
    // 條目的佈局檔案中有哪些控制元件,這裡就定義什麼成員變數
    TextView tv_name;
    TextView tv_phone;
    TextView tv_salary;
}

   接下來,只要在getView()方法中將ViewHolder封裝至條目快取convertView,當快取存在時直接複用convertView並從中取出所封裝的ViewHolder,即可避免不必要的findViewById()

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    ViewHolder holder;
    if (convertView == null) {
        convertView = View.inflate(MainActivity.this, R.layout.item_listview, null);
        holder = new ViewHolder();
        // 把佈局檔案中所有控制元件封裝至ViewHolder中
        holder.tv_name = (TextView) convertView.findViewById(R.id.tv_name);
        holder.tv_phone = (TextView) convertView.findViewById(R.id.tv_phone);
        holder.tv_salary = (TextView) convertView.findViewById(R.id.tv_salary);
        convertView.setTag(holder); // 把ViewHolder封裝至View物件,最後會封裝至條目快取convertView
    } else {
        holder = (ViewHolder) convertView.getTag();// 快取的View物件中攜帶著ViewHolder,可以避免頻繁的findViewById操作
    }
    Teacher t = list.get(position);
    holder.tv_name.setText(t.getName());
    holder.tv_phone.setText(t.getPhone());
    holder.tv_salary.setText(t.getSalary());
    return convertView;
}

對話方塊

確定取消對話方塊

  建立對話方塊構建器物件,類似工廠模式

AlertDialog.Builder builder = new Builder(this);

  設定標題和正文

builder.setTitle("警告");
builder.setMessage("若練此功,必先自宮");

  設定確定和取消按鈕

builder.setPositiveButton("現在自宮", new OnClickListener() {

    @Override
    public void onClick(DialogInterface dialog, int which) {
        // TODO Auto-generated method stub
        Toast.makeText(MainActivity.this, "恭喜你自宮成功,現在程式退出", 0).show();
    }
});

builder.setNegativeButton("下次再說", new OnClickListener() {

    @Override
    public void onClick(DialogInterface dialog, int which) {
        // TODO Auto-generated method stub
        Toast.makeText(MainActivity.this, "若不自宮,一定不成功", 0).show();
    }
});

  使用構建器創建出對話方塊物件

AlertDialog ad = builder.create();
ad.show();

單選對話方塊

AlertDialog.Builder builder = new Builder(this);
builder.setTitle("選擇你的性別");

  定義單選選項

final String[] items = new String[]{
        "男", 
        "女"
};
//-1表示預設誰也不選中
//點選偵聽的導包要注意別導錯
builder.setSingleChoiceItems(items, -1, new OnClickListener() {

    //dialog:觸發這個方法的對話方塊
    //which:使用者所選的條目的下標
    @Override
    public void onClick(DialogInterface dialog, int which) {
        Toast.makeText(MainActivity.this, "您選擇了" + items[which], 0).show();
        //關閉對話方塊
        dialog.dismiss();
    }
});
//可以直接用構建器顯示對話方塊
builder.show();

多選對話方塊

AlertDialog.Builder builder = new Builder(this);
builder.setTitle("請選擇你覺得帥的人");

  定義多選的選項,因為可以多選,所以需要一個boolean陣列來記錄哪些選項被選了

final String[] items = new String[]{
        "吳彥祖",
        "吳亦凡",
        "劉德華",
        "古天樂"
};
//預設只選中第一個條目
final boolean[] checkedItems = new boolean[]{
        true,
        false,
        false,
        false,
};
builder.setMultiChoiceItems(items, checkedItems, new OnMultiChoiceClickListener() {

    //which:使用者點選條目的下標
    //isChecked:用於判斷使用者點選該條目是選中還是取消
    @Override
    public void onClick(DialogInterface dialog, int which, boolean isChecked) {
        checkedItems[which] = isChecked;
    }
});
//設定一個確定按鈕
builder.setPositiveButton("確定", new OnClickListener() {

    @Override
    public void onClick(DialogInterface dialog, int which) {
        StringBuffer sb = new StringBuffer();
        for(int i = 0;i < items.length; i++){
            sb.append(checkedItems[i] ? items[i] + " " : "");
        }
        Toast.makeText(MainActivity.this, sb.toString(), 0).show();
    }
});
builder.show();