設(shè)計思路
筆記本與筆記是一對多的關(guān)系:
- 每條筆記屬于一個筆記本
- 一個筆記本包含多條筆記
1. 數(shù)據(jù)庫設(shè)計
首先,我們需要為筆記本創(chuàng)建新的表,將其命名為notebook。我們簡單的將其設(shè)計為具有以下字段:
- id: 一個筆記本的唯一id
- name: 筆記本的名字
其次,我們要為筆記表(note)增加與筆記本表的關(guān)聯(lián)。則在創(chuàng)建note表時增加一列notebookId,即某條筆記對應(yīng)的筆記本的id
2. 數(shù)據(jù)庫修改
打開前面章節(jié)中創(chuàng)建的NoteDAO類,找到其中的內(nèi)部類NoteDbHelper,修改它的onCreate(),修改note表創(chuàng)建語句,在最后增加notebookId列,并添加創(chuàng)建notebook表的語句:
@Override
public void onCreate(SQLiteDatabase db) {
// 創(chuàng)建note表
db.execSQL("CREATE TABLE " + TABLE_NOTE + "(" +
COL_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
COL_TITLE + " TEXT, " +
COL_CONTENT + " TEXT, " +
COL_CREATE_TIME + " INTEGER, " +
COL_NOTEBOOK_ID + " INTEGER)");
// 創(chuàng)建notebook表
db.execSQL("CREATE TABLE " + TABLE_NOTEBOOK + "(" +
COL_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
COL_NOTEBOOK_NAME + " TEXT)");
}
出現(xiàn)了三個新的常量,分別是筆記本表的表名常量TABLE_NOTEBOOK,以及筆記本名字對應(yīng)的列名常量COL_NOTEBOOK_NAME。打開與NoteDAO類同一包下的Constants類,在末尾添加這兩個常量:
public class Constants {
...
public static final String COL_NOTEBOOK_ID = "notebookId";
public static final String TABLE_NOTEBOOK = "notebook";
public static final String COL_NOTEBOOK_NAME= "name";
}
另外,根據(jù)對筆記表的修改,我們應(yīng)當(dāng)相應(yīng)的修改Note類,增加notebookId屬性以及對應(yīng)的訪問方法。打開Note類,增加以下內(nèi)容,其它舊有部分保持不變:
public class Note {
...
/**
* 所屬的筆記本的id
*/
private long notebookId;
...
public Note(long id, String title, String content, long createTim, long notebookId) {
this.id = id;
this.title = title;
this.content = content;
this.createTime = createTime;
this.notebookId = notebookId;
}
...
public long getNotebookId() {
return notebookId;
}
public void setNotebookId(long noteId) {
notebookId = notebookId;
}
}
可以看到,我們還增加了一個構(gòu)造方法,將notebookId的初始化包含進(jìn)去。原來已有的構(gòu)造方法保持不變。
運(yùn)行代碼之前,將原來版本的App從模擬器或者手機(jī)中刪除。原因是,數(shù)據(jù)庫結(jié)構(gòu)變更并不會自動觸發(fā)升級操作。而數(shù)據(jù)庫升級操作相對復(fù)雜,此處我們先不考慮,只是簡單的卸載后重新運(yùn)行程序。這樣做的弊端是,原來已有的數(shù)據(jù)全部丟失。但是對于目前階段,我們的數(shù)據(jù)主要是測試數(shù)據(jù),因此影響不大。
接下來,由于數(shù)據(jù)模型發(fā)生變化,我們的數(shù)據(jù)訪問方法也要進(jìn)行相應(yīng)的修改。再次打開NoteDAO類,依次修改如下方法:
- insertNote()
- queryNoteById()
- queryAllNotes()
insertNote()
在向note表插入筆記時,增加代碼以寫入notebookId屬性:
public Note insertNote(Note note) {
...
ContentValues values = new ContentValues();
values.put(COL_TITLE, note.getTitle());
values.put(COL_CONTENT, note.getContent());
values.put(COL_CREATE_TIME, note.getCreateTime());
// 將notebookId寫入數(shù)據(jù)庫
values.put(COL_NOTEBOOK_ID, note.getNotebookId());
...
}
queryNoteById()
public Note queryNoteById(long id) {
...
String title = cursor.getString(1);
String content = cursor.getString(2);
// 讀取notebookId記錄
long notebookId = cursor.getLong(4);
Note note = new Note(id, title, content, createTime, notebookId);
return note;
...
}
queryAllNotes()
public List<Note> queryAllNotes() {
...
long id = cursor.getLong(0);
String title = cursor.getString(1);
String content = cursor.getString(2);
long createTime = cursor.getLong(3);
// 讀取notebookId
long notebookId = cursor.getLong(4);
Note note = new Note(id, title, content, createTime, notebookId);
notes.add(note);
...
}
運(yùn)行代碼,應(yīng)用原有功能應(yīng)當(dāng)保持不變。
2. 創(chuàng)建筆記本列表頁面
用ActionBar圖標(biāo)按鈕來作為筆記本列表入口:

在點擊右上角筆記本按鈕后,打開一個新的activity,我們將其命名為NotebooksActivity。在這個activity中,完成以下功能:
- 展示全部筆記本
- 創(chuàng)建新的筆記本
- 選擇筆記本,并向調(diào)用NotebooksActivity的上一級activity返回對應(yīng)的筆記本ID
在我們的項目包名上右鍵選擇“New -> Activity -> Empty Activity”,在彈出的對話框中填寫名稱并確認(rèn):

點擊“Finish”,NotebooksActivity.java文件以及對應(yīng)的布局文件自動創(chuàng)建完畢。
將NotebooksActivity的標(biāo)題設(shè)置為“筆記本”(還記得怎么做嗎?):
<activity
android:name=".NotebooksActivity"
android:label="@string/notebook"/>
現(xiàn)在打開全部筆記頁面,添加ActionBar上的筆記本按鈕。打開NoteListActivity類,添加菜單處理相關(guān)的兩個方法:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.menu_note_list, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_notebook:
return true;
}
return super.onOptionsItemSelected(item);
}
此時,菜單資源文件menu_note_list.xml并不存在,menu_item_notebook菜單項必然也不存在,編輯界面會提示錯誤:

下面我們創(chuàng)建它們。在res目錄下找到menu目錄,里面已經(jīng)有我們之前為新建筆記頁面創(chuàng)建的菜單文件。我們現(xiàn)在新建一個名為menu_note_list.xml的新菜單文件,方法是右鍵單擊menu文件夾,在彈出的菜單中選擇“New -> Menu resource file”,在彈出的對話框中輸入文件名:

點擊OK完成,則創(chuàng)建好了空菜單文件:

打開編輯,為其中添加如下的菜單項:
<item
android:id="@+id/menu_item_notebook"
android:title="@string/notebook"
android:icon="@drawable/ic_notebook"
app:showAsAction="always"/>
其中:
- 菜單項id為menu_item_notebook
- 菜單項文字為“筆記本”字符串
- “app:showAsAction”屬性表示將菜單項顯示為圖標(biāo)按鈕
- “android:icon”屬性是對應(yīng)的圖標(biāo),設(shè)置為“@drawable/ic_notebook”

下載上面的圖片,放到你的res/drawable-xxhdpi文件夾下即可。
現(xiàn)在我們實現(xiàn)筆記本按鈕的操作,也就是啟動我們剛才創(chuàng)建的筆記本列表頁面。但是,這回不是簡單的啟動,而是要在筆記本列表中選擇某個筆記本后將筆記本的id返回回來,于是,要用startActivityForResult()方法代替startActivity()方法來啟動筆記本列表頁面。
在onOptionsItemSelected()方法中編寫代碼如下:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_notebook:
// 添加如下代碼:
Intent intent = new Intent(this, NotebooksActivity.class);
startActivityForResult(intent, 1);
return true;
}
return super.onOptionsItemSelected(item);
}
可以看到,startActivityForResult()多了一個參數(shù)“1”,這代表這次操作的請求碼,據(jù)此在讀取返回結(jié)果時判斷來自哪個activity。至于如何處理返回結(jié)果,我們隨后再討論,現(xiàn)在簡單的看一下啟動筆記本列表頁面的效果:
