- 通過開發App熟悉Android Studio的用法
- 開發一款用於管理備忘事項列表的App,核心功能:
- 創建、刪除備忘
- 將某些備忘標記為重要(左側帶顏色標簽突出顯示)
- 涉及:操作欄菜單、上下文菜單、用於持久化的本地數據庫、支持多選的設備上的多項選擇
啟動新項目
File|New|New project命名為Reminders,選擇Empty Activity
初始化Git倉庫
- 創建Git倉庫
然后選擇項目根目錄來創建Git倉庫
-
在Version Control工具窗口中右擊Unversioned Files選擇Add to VCS將這些文件添加到Git索引
-
提交文件(
Ctrl+K
|將項目的修改記錄到Git版本控制系統的過程)
構建用戶界面
visual designer布局
可視化設計器
將左側Palette\Legacy中的ListView拖到編輯區域放置該控件,右側Attributes修改該控件各項屬性
編輯布局的原始XML
單擊底部Text,編輯原始XML
更改背景顏色:
android:background="#181818" //設置RelativeLayout背景顏色
在XML布局文件中硬編碼顏色值並不是最佳方案,更好的方法:在values資源文件夾下定義colors.xml,在里面定義自己的顏色。這樣便於編輯而且可以很容易地在整個項目中引用。
將代碼修改為:
android:background="@color/dark_grey"
在錯誤提示中選擇第二項(創建顏色資源)
修改ListView列表項的顯示方式
在res\layout文件夾中新建reminders_row,選擇LinearLayout(LinearLayout是布局中的最外層元素)作為根ViewGroup來為單個列表項行創建布局,
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="50dp" android:orientation="vertical" android:background="@color/dark_grey"> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="48dp"> <view android:layout_width="10dp" android:layout_height="match_parent" class="android.view.View" android:id="@+id/row_tab" android:background="@color/green"/> <TextView android:layout_width="match_parent" android:layout_height="50dp" android:text="Reminder Text" android:id="@+id/row_text" android:textColor="@color/white" android:textSize="18sp" android:gravity="center_vertical" android:padding="10dp" android:ellipsize="end" android:maxLines="1"/> </LinearLayout> <view class="android.view.View" android:layout_width="fill_parent" android:layout_height="1dp" android:background="#000"/> <view class="android.view.View" android:layout_width="fill_parent" android:layout_height="1dp" android:background="#333"/> </LinearLayout>
向ListView添加條目
修改相應的Activity文件(RemindersActivity.java),聲明一個ListView成員、修改onCreate()方法
private ListView mListView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_reminders); mListView = (ListView)findViewById(R.id.reminders_list_view); ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>( this, // 當前Activity的Context對象 R.layout.reminders_row, //使用哪個布局 R.id.row_text, //布局中的哪個字段來顯示數據 new String[]{"first record","second record"} //示例條目 ); mListView.setAdapter(arrayAdapter); }
添加操作欄溢出菜單
在res文件夾下新建menu文件夾,右鍵New|Menu resource file命名為menu_reminders
menu/menu_reminders.xml:
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/action_new" android:title="New Reminder" android:orderInCategory="100" app:showAsAction="never"/> <item android:id="@+id/action_exit" android:title="exit" android:orderInCategory="200" app:showAsAction="never"/> </menu>
在RemindersActivity.java中添加創建菜單的方法和菜單的點擊事件
/** *創建菜單 */ @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_reminders,menu); //通過getMenuInflater()方法得到MenuInflater對象,再調用它的inflate()方法就可以給當前活動創建菜單了,第一個參數:用於指定我們通過哪一個資源文件來創建菜單;第二個參數:用於指定我們的菜單項將添加到哪一個Menu對象當中。 return true; // true:允許創建的菜單顯示出來,false:創建的菜單將無法顯示。 } /** *菜單的點擊事件 */ @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()){ case R.id.action_new: Log.d(getLocalClassName(),"create new Reminder"); return true; case R.id.action_exit: finish(); return true; default: return false; } }
持久化備忘錄
- SQLite數據庫
- 數據模型、數據庫代理類、CursorAdapter
- 數據模型:保存從數據庫讀取以及寫入數據庫的數據
- 數據庫代理類:適配器類,把來自App的簡單調用轉換為對SQLite數據庫的API調用
- CursorAdapter:繼承以抽象方式處理數據訪問的標准Android類
數據模型
創建數據模型
com.example.dell.reminders右鍵New Java Class命名為Reminder
Reminder類:
public class Reminder { private int mId; private String mContent; private int mImportant; public Reminder(int id, String content, int important) { mId = id; mContent = content; mImportant = important; } public int getId() { return mId; } public void setId(int id) { mId = id; } public String getContent() { return mContent; } public void setContent(String content) { mContent = content; } public int getImportant() { return mImportant; } public void setImportant(int important) { mImportant = important; } }
創建數據庫代理
com.example.dell.reminders右鍵New Java Class命名為RemindersDbAdapter
public class RemindersDbAdapter { //定義列名 public static final String COL_ID = "_id"; public static final String COL_CONTENT = "content"; public static final String COL_IMPORTANT = "important"; //定義索引值 public static final int INDEX_ID = 0; public static final int INDEX_CONTENT = INDEX_ID + 1; public static final int INDEX_IMPORTANT = INDEX_ID + 2; //用於日志的TAG public static final String TAG = "RemindersDbAdapter"; //兩個數據庫API對象 private DatabaseHelper mDbHelper; private SQLiteDatabase mDb; //數據庫名稱、主表名稱、版本的常量 public static final String DATABASE_NAME = "dba_remdrs"; public static final String TABLE_NAME = "tbl_remdrs"; public static final int DATABASE_VERSION = 1; //上下文對象 private final Context mCtx; //用於創建數據庫的SQL語句 private static final String DATABASE_CREATE = "CREATE TABLE if not exists " + TABLE_NAME + " ( " + COL_ID + "INTEGER PRIMARY KEY autoincrement, " + COL_CONTENT + " TEXT, " + COL_IMPORTANT + " INTEGER );"; }
SQLite API
DatabaseHelper:用於打開、關閉數據庫的SQLite API類,是一個自定義的類,將其實現為RemindersDbAdapter的內部類
private static class DatabaseHelper extends SQLiteOpenHelper{ //構造函數完成數據庫初始化 DatabaseHelper(Context context){ //將數據庫名和版本號傳給超類,由超類完成建立數據庫的繁重工作 super(context,DATABASE_NAME,null,DATABASE_VERSION); } public void onCreate(SQLiteDatabase db){ Log.w(TAG,DATABASE_CREATE); db.execSQL(DATABASE_CREATE); } public void onUpgrade(SQLiteDatabase db,int oldVersion, int newVersion){ Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data"); db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); onCreate(db); } }
使用DatabaseHelper打開和關閉數據庫,RemindersDbAdapter構造函數保存了Context實例,並傳給DatabaseHelper
public RemindersDbAdapter(Context ctx) { this.mCtx = ctx; } public void open() throws SQLException{ mDbHelper = new DatabaseHelper(mCtx); mDb = mDbHelper.getWritableDatabase(); } public void close(){ if(mDbHelper != null){ mDbHelper.close(); } }
數據庫的增刪改查:
創建方法使用特殊的ContentValues對象,用於將數據值傳給數據庫對象的insert方法,數據庫會將這些對象轉換為SQL insert語句並執行
//數據庫的增刪改查 public void createReminder(String name, boolean important){ ContentValues values = new ContentValues(); values.put(COL_CONTENT, name); values.put(COL_IMPORTANT, important?1:0); mDb.insert(TABLE_NAME,null,values); } public long createReminder(Reminder reminder){ ContentValues values = new ContentValues(); values.put(COL_CONTENT,reminder.getContent()); values.put(COL_IMPORTANT,reminder.getImportant()); return mDb.insert(TABLE_NAME,null,values); } public Reminder fetchReminderById(int id){ Cursor cursor = mDb.query(TABLE_NAME,new String[]{COL_ID, COL_CONTENT,COL_IMPORTANT},COL_ID + "=?", new String[]{String.valueOf(id)},null,null,null,null); if(cursor != null) cursor.moveToFirst(); return new Reminder( cursor.getInt(INDEX_ID), cursor.getString(INDEX_CONTENT), cursor.getInt(INDEX_IMPORTANT) ); } public Cursor fetchAllReminders(){ Cursor mCursor = mDb.query(TABLE_NAME,new String[]{COL_ID, COL_CONTENT,COL_IMPORTANT},null,null,null,null,null); if (mCursor != null){ mCursor.moveToFirst(); } return mCursor; } public void updateReminder(Reminder reminder){ ContentValues values = new ContentValues(); values.put(COL_CONTENT,reminder.getContent()); values.put(COL_IMPORTANT,reminder.getImportant()); mDb.update(TABLE_NAME,values, COL_ID+"=?",new String[]{String.valueOf(reminder.getId())}); } public void deleteReminderById(int nId){ mDb.delete(TABLE_NAME,COL_ID+"=?",new String[]{String.valueOf(nId)}); } public void deleteAllReminders(){ mDb.delete(TABLE_NAME,null,null); }
RemindersDbAdapter最終代碼
public class RemindersDbAdapter {
//定義列名
public static final String COL_ID = "_id";
public static final String COL_CONTENT = "content";
public static final String COL_IMPORTANT = "important";
//定義索引值
public static final int INDEX_ID = 0;
public static final int INDEX_CONTENT = INDEX_ID + 1;
public static final int INDEX_IMPORTANT = INDEX_ID + 2;
//用於日志的TAG
public static final String TAG = "RemindersDbAdapter";
//兩個數據庫API對象
private DatabaseHelper mDbHelper;
private SQLiteDatabase mDb;
//數據庫名稱、主表名稱、版本的常量
public static final String DATABASE_NAME = "dba_remdrs";
public static final String TABLE_NAME = "tbl_remdrs";
public static final int DATABASE_VERSION = 1;
//上下文對象
private final Context mCtx;
//用於創建數據庫的SQL語句
private static final String DATABASE_CREATE =
"CREATE TABLE if not exists " + TABLE_NAME + " ( " +
COL_ID + "INTEGER PRIMARY KEY autoincrement, " +
COL_CONTENT + " TEXT, " +
COL_IMPORTANT + " INTEGER );";
public RemindersDbAdapter(Context ctx) {
this.mCtx = ctx;
}
public void open() throws SQLException{
mDbHelper = new DatabaseHelper(mCtx);
mDb = mDbHelper.getWritableDatabase();
}
public void close(){
if(mDbHelper != null){
mDbHelper.close();
}
}
//數據庫的增刪改查
public void createReminder(String name, boolean important){
ContentValues values = new ContentValues();
values.put(COL_CONTENT, name);
values.put(COL_IMPORTANT, important?1:0);
mDb.insert(TABLE_NAME,null,values);
}
public long createReminder(Reminder reminder){
ContentValues values = new ContentValues();
values.put(COL_CONTENT,reminder.getContent());
values.put(COL_IMPORTANT,reminder.getImportant());
return mDb.insert(TABLE_NAME,null,values);
}
public Reminder fetchReminderById(int id){
Cursor cursor = mDb.query(TABLE_NAME,new String[]{COL_ID,
COL_CONTENT,COL_IMPORTANT},COL_ID + "=?",
new String[]{String.valueOf(id)},null,null,null,null);
if(cursor != null)
cursor.moveToFirst();
return new Reminder(
cursor.getInt(INDEX_ID),
cursor.getString(INDEX_CONTENT),
cursor.getInt(INDEX_IMPORTANT)
);
}
public Cursor fetchAllReminders(){
Cursor mCursor = mDb.query(TABLE_NAME,new String[]{COL_ID,
COL_CONTENT,COL_IMPORTANT},null,null,null,null,null);
if (mCursor != null){
mCursor.moveToFirst();
}
return mCursor;
}
public void updateReminder(Reminder reminder){
ContentValues values = new ContentValues();
values.put(COL_CONTENT,reminder.getContent());
values.put(COL_IMPORTANT,reminder.getImportant());
mDb.update(TABLE_NAME,values,
COL_ID+"=?",new String[]{String.valueOf(reminder.getId())});
}
public void deleteReminderById(int nId){
mDb.delete(TABLE_NAME,COL_ID+"=?",new String[]{String.valueOf(nId)});
}
public void deleteAllReminders(){
mDb.delete(TABLE_NAME,null,null);
}
private static class DatabaseHelper extends SQLiteOpenHelper{
//構造函數完成數據庫初始化
DatabaseHelper(Context context){
//將數據庫名和版本號傳給超類,由超類完成建立數據庫的繁重工作
super(context,DATABASE_NAME,null,DATABASE_VERSION);
}
public void onCreate(SQLiteDatabase db){
Log.w(TAG,DATABASE_CREATE);
db.execSQL(DATABASE_CREATE);
}
public void onUpgrade(SQLiteDatabase db,int oldVersion, int newVersion){
Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
}
}
CursorAdapter
最后,需要一種從數據庫獲取備忘並加入到ListView中的方法,新建java類RemindersSimpleCursorAdapter
public class RemindersSimpleCursorAdapter extends SimpleCursorAdapter { public RemindersSimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags) { super(context, layout, c, from, to, flags); } @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { return super.newView(context, cursor, parent); } //ListView會利用屏幕上的單個View對象反復調用此方法,Adapter的職責就是使用列表項來填充這些視圖 @Override public void bindView(View view, Context context, Cursor cursor) { super.bindView(view, context, cursor);//調用超類方法,通過游標cursor獲取到的值映射到View中的元素 ViewHolder holder = (ViewHolder)view.getTag(); if(holder == null){ //檢查holder是否綁定到了標簽 holder = new ViewHolder(); holder.colImp = cursor.getColumnIndexOrThrow(RemindersDbAdapter.COL_IMPORTANT); holder.listTab = view.findViewById(R.id.row_tab); view.setTag(holder); } //使用當前備忘COL_IMPORTANT常量對應的值來決定顏色1:重要 0:次要 if(cursor.getInt(holder.colImp) > 0){ holder.listTab.setBackgroundColor(context.getResources().getColor(R.color.orange)); } else{ holder.listTab.setBackgroundColor(context.getResources().getColor(R.color.green)); } } //靜態內部類 static class ViewHolder{ int colImp; //Important表列的索引 View listTab; //在布局中定義的row_tab視圖 } }
調整ReminderActivity
public class RemindersActivity extends AppCompatActivity { private ListView mListView; private RemindersDbAdapter mDbAdapter; private RemindersSimpleCursorAdapter mCursorAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_reminders); mListView = (ListView)findViewById(R.id.reminders_list_view); mListView.setDivider(null); mDbAdapter = new RemindersDbAdapter(this); mDbAdapter.open(); Cursor cursor = mDbAdapter.fetchAllReminders(); String[] from = new String[]{RemindersDbAdapter.COL_CONTENT}; int[] to = new int[]{R.id.row_text}; mCursorAdapter = new RemindersSimpleCursorAdapter( RemindersActivity.this, R.layout.reminders_row, cursor, from, to, 0 ); mListView.setAdapter(mCursorAdapter); } }
調整完運行app,將不會在列表中看到任何內容,因為最后的修改插入的是SQLite功能而非示例數據
Ctrl+K
提交備忘錄實驗1的最后一次修改