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();