NotePad Exercise 1

Trong bài tập này, bạn sẽ construct một notes list đơn giãn cho phép user thêm mới các notes nhưng không được edit chúng. Trong bài này, chúng ta sẽ làm hiểu các phần cơ bản sau:

  • Cơ bản về ListActivity, tạo và xử lý menu options.
  • Sử dụng SQLite database để lưu trữ các notes.
  • Bind dữ liệu từ một database cursor vào một ListView sử dụng một SimpleCursorAdapter.
  • Cơ bản về screen layout (layout một list view), thêm các item vào activity menu, và cách thức activity xử lý các selection cho menu.

Trước khi bắt đầu, bạn vào đây để download bài tập có chứa NotepadCodeLab.

I.Step 1

  • B1.Trong Eclipse, click vào File->New->Android Project.
  • B2.Chọn Create project from existing source.
  • B3.Click Browse và navigate đến nơi có chứa thư mục NotepadCodeLab và chọn Notepadv1.
  • B4.Click Finish.

II.Step 2

  • Sau khi tạo project Notepadv1, bạn sẽ thấy lớp NotesDbAdapter – lớp này được cung cấp để đóng gói dữ liệu truy cập vào SQLite database mà sẽ nắm giữ các notes data và cho phép bạn cập nhật nó.
  • Ở phía trên của lớp là một số định nghĩa constant sẽ được sử dụng trong ứng dụng để tìm kiếm dữ liệu từ các tên trường thích hợp trong database.
  • Tên của database là “data” và tên của table là “notes” gồm có 3 field: _id, title và body.
  • Constructor của NotesDbAdapter có chứa một Context, cho phép nó giao tiếp với các khía cạnh của OS Android. Lớp Activity implement lớp Context, vì vậy bạn thường sẽ sử dụng từ khóa “this” từ Activity, khi cần một Context.
  • Phương thức open() gọi một thể hiện của lớp DatabaseHelper (lớp này được cài đặt cục bộ trong lớp NotesDbAdapter và kết thừa từ SQLiteOpenHelper). Nó gọi getWritableDatabase() để xử lý việc tạo và mở database cho bạn.
  • Phương thức createNote() có chứa các string cho title và body của một note mới, sau đó tạo note đó trong database. Giả sử note mới được tạo ra thành công, phương thức này cũng trả về giá trị _id cho note mới được tạo ra.
  • Phương thức deleteNote() có chứa một rowId cho một note cụ thể, và xóa note đó từ database.
  • Phương thức fetchAllNote() thực hiện truy vấn và trả về một Cursor cho tất cả các note trong database, đó là nó gọi phương thức query(). Trong phương thức query(), tham số đầu tiên là tên của database table để truy vấn (trong ví dụ này, DATABASE_TABLE là “notes”), tham số tiếp theo là danh sách các column mà bạn muốn trả về, trong ví dụ này đó là các column: _id, title, và body, vì vậy chúng được xác định là một mảng String. Các tham số thiếp theo lần lượt là: selection, selectionArgs, groupBy, having và orderBy; giá trị của having bằng null, có nghĩa là bạn muốn tất cả data, không cần group chúng và có thứ tự mặc định. Xem thêm SQLiteDatabase.
  • Lưu ý: Một Cursor được trả về chứ không phải là một collection của các row. Điều này cho phép Android sử dụng các resource một cách hiệu quả – thay vì put các dữ liệu trực tiếp vào bộ nhớ, con trỏ sẽ retrieve và release dữ liệu khi cần thiết, điều này hiệu quả hơn nhiều việc sử dụng các table có chứa nhiều row.
  • Phương thức fetchNote() tương tự như fetchAllNote() nhưng chỉ nhận một note với rowId.
  • Cuối cùng, phương thức updateNote() có chứa rowId, title và body, và sử dụng một thể hiện của ContentValues để cập nhật note của rowId cho trước.

III.Step 3

Mở file notepad_list.xml trong thư mục “res/layout”, dưới đây là một số điều bạn nên biết về file layout này:

  • Tất cả các file layout trong Android đều bắt đầu với dòng XML header:
<?xml version=“1.0” encoding=“utf-8”?>
  • Dòng tiếp theo thường là một định nghĩa layout, trong ví dụ này là LinearLayout.
  • Namespace XML của Android luôn luôn được định nghĩa tại top level của component hay layout trong XML để các tags “android:” có thể được sử dụng thông qua phần còn lại của file.
xmlns:android=http://schemas.android.com/apk/res/android&#8221;

IV.Step 4

Bây giờ, bạn cần tạo ra layout để nắm giữ list. Thêm đoạn mã sau vào bên trong phần tử LinearLayout như sau:

<?xml version=“1.0” encoding=“utf-8”?> 

<LinearLayout xmlns:android=http://schemas.android.com/apk/res/android&#8221;

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”>

<ListView android:id=“@android:id/list”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”/>

<TextView android:id=“@android:id/empty”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:text=“@string/no_notes”/>

</LinearLayout>

  • Biểu tượng @ trong id strings của các tag ListView và TextView có nghĩa là XML parser nên parse và expand phần còn lại của id string và sử dụng một ID resource.
  • ListView và TextView có thể được xem như là hai view khác nhau, chỉ một trong số đó sẽ được hiển thị tại một thời điểm. ListView sẽ được sử dụng khi có các notes được hiển thị, trong khi TextView (trong đó có một giá trị mặc ​​định là “No Notes Yet!” được định nghĩa như là một string resource trong res/values/strings.xml) sẽ được hiển thị nếu không có bất kỳ note nào để hiển thị.
    Các ID list và empty được cung cấp cho bạn bằng Android platform, vì vậy, chúng ta phải prefix id với android: (ví dụ, @android:id/list).
    View với id empty được sử dụng tự động khi ListAdapter không có dữ liệu cho ListView. Ngoài ra, bạn có thể thay view empty mặc định bằng cách sử dụng setEmptyView(View) trên ListView.
  • Nói rộng ra, lớp android.R là một tập hợp các resource được định nghĩa trước cung cấp cho bạn bởi platform, trong khi lớp R của project của bạn là tập hợp các resource project của bạn đã được định nghĩa. Các resource có trong lớp resource android.R có thể được sử dụng trong các file XML bằng cách sử dụng prefix name space “android:” (như chúng ta thấy ở đây).

V.Step 5

Để tạo ra danh sách các notes trong ListView, bạn cần định nghĩa một View cho một row:

B1.Tạo một layout resource mới “notes_row.xml” trong thư mục “res/layout”.

B2.Thêm các nội dung sau đây file vừa tạo:

<?xml version=“1.0” encoding=“utf-8”?> 

<TextView android:id=“@+id/text1”

xmlns:android=http://schemas.android.com/apk/res/android&#8221;

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”/>

  • Đây là View được sử dụng để hiển thị mỗi notes title row – chỉ có một text field bên trong nó.
  • Trong trường hợp này, bạn tạo ra một id mới gọi là text1. Dấu “+” sau “@” trong id string cho biết rằng id nên được tạo ra tự động như là một resource nếu nó không tồn tại, vì vậy chúng ta định nghĩa nó để sử dụng.

B3.Lưu file.

Mở lớp R.java trong project của bạn, bạn sẽ thấy các định nghĩa mới cho notes_row và text1, có nghĩa là bây giờ chúng ta có thể truy cập vào chúng trong code.

VI.Step 6

  • Tiếp theo, mở lớp Notepadv1. Trong bước này, bạn sẽ sửa đổi nó để trở thành một list adapter và hiển thị các notes, đồng thời cho phép thêm note mới.
  • Notepadv1 kế thừa từ một lớp con của Activity, đó là ListActivity, trong đó có chức năng bổ sung cho phù hợp với loại công việc mà bạn có thể muốn làm việc trên một list, ví dụ: hiển thị các list item  trong các row lên màn hình, di chuyển qua lại giữa các list item, và cho phép lựa chọn trên chúng.
  • Trong lớp này, có một biến mNoteNumber được sử dụng để tạo ra các note title được đánh số (numbered). Đồng thời, chúng có 3 phương thức sẽ được override sau đây:
    • onCreate() được gọi khi activity được start, nó được sử dụng để thiết lập (set up) các resource và state cho activity khi nó đang chạy.
    • onCreateOptionsMenu() được sử dụng để populate menu cho Activity. Menu sẽ được hiển thị khi user nhấn phím MENU trên thiết bị, nó có một danh sách các option để user có thể chọn.
    • onOptionsItemSelected() được sử dụng để xử lý các sự kiện phát sinh khi user chọn một item trong Menu.

VII.Step 7

  • Thay đổi lớp Notepadv1 kế thừa ListActivity thay vì Activity.
public class Notepadv1 extends ListActivity {
  • Lưu ý: Bạn sẽ phải import ListActivity vào Notepadv1 sử dụng Eclipse, nhấn tổ hợp phím ctrl-shift-O trên Windows hay Linux, hay cmd-shift-O trên Mac.

VIII.Step 8

Cài đặt phương thức onCreate().

Ở đây bạn sẽ cài đặt title cho Activity (hiển thị ở top của màn hình), sử dụng layout “notepad_list” mà bạn đã tạo ra trước đó, thiết lập một thể hiện của NotesDbAdapter để truy cập dữ liệu note, và populate danh sách với các note title sẵn có:

  • B1.Trong phương thức onCreate(), gọi super.onCreate() với tham số saveInstanceState.
  • B2.Gọi setContentView() và gán R.layout.notepad_list.
  • B3.Tại top của lớp, tạo một biến lớp private mới mDbHelper của lớp NotesDbAdapter.
  • B4.Trở lại phương thức onCreate(), tạo ra một đối tượng mới NotesDbAdapter và gán nó cho trường mDbHelper.
  • B5.Gọi phương thức open() trên mDbHelper để mở (hay tạo mới) database.
  • B6.Cuối cùng, tạo mới một phương thức fillData() để nhận dữ liệu và populate ListView sử dụng helper – chúng ta vẫn chưa định nghĩa phương thức này.

Phương thức onCreate() sẽ trông như sau:

@Override 

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.notepad_list);

mDbHelper = new NotesDbAdapter(this);

mDbHelper.open();

fillData();

}

Và đảm bảo rằng, bạn đã khai báo mDbHelper:

private NotesDbAdapter mDbHelper;

IX.Step 9

Cài đặt phương thức onCreateOptionsMenu().

Bây giờ, bạn sẽ tạo button “Add item” được truy cập bằng cách nhấn menu button trên thiết bị. Và chúng ta xác định rằng nó chiếm vị trí đầu tiên trong Menu.

  • B1.Trong resource “strings.xml” (trong thư mục “res/values”), và thêm một string mới được đặt tên “menu_insert” với giá trị của nó là “Add Item”.
<string name=“menu_insert”>Add Item</string>
  • Sau đó lưu file và trở về Notepadv1.
  • B2.Tạo một hằng menu position tại top của lớp:
public static final int INSERT_ID = Menu.FIRST;
  • B3.Trong phương thức onCreateOptionsMenu(), khai báo một biến cục bộ result kiểu boolean và gán cho super.onCreateOptionsMenu().
  • B4.Sau đó, thêm menu item với phương thức add().
  • Bây giờ phương thức onCreateOptionsMenu() trông như sau:
@Override 

public boolean onCreateOptionsMenu(Menu menu) {

boolean result = super.onCreateOptionsMenu(menu);

menu.add(0, INSERT_ID, 0, R.string.menu_insert);

return result;

}

  • Các đối số được gán vào trong phương thức add() cho biết: một group identifier, một unique ID, thứ tự của item và resource của string để sử dụng cho item.

X.Step 10

Cài đặt phương thức onOptionsItemSelected().

Trong phần này, chúng ta sẽ xử lý menu item “Add Note”. Khi item này trên Menu được chọn, phương thức onOptionsItemSelected() sẽ được gọi với item.getId() thiết lập cho INSERT_ID (hằng mà bạn sử dụng để identify menu item).

  • B1.Gọi super.onOptionsItemSelected() vào cuối phương thức này.
  • B2.Viết một biểu thức “switch” dựa vào item.getId().

Phương thức onOptionsItemSelected() trông như sau:

@Override 

public boolean onOptionsItemSelected(MenuItem item) {

switch (item.getItemId()) {

case INSERT_ID:

createNote();

return true;

}

return super.onOptionsItemSelected(item);

}

XI.Step 11

Cài đặt phương thức createNote().

Trong phương thức này, bạn đơn giãn chỉ tạo một note mới với title được gán dựa trên một counter (ví dụ, “Note 1”, “Note 2”…) với một body trống.

  • B1.Xây dựng name với “Note” và counter được định nghĩa trong lớp này:
String noteName = “Note ” + mNoteNumber++;
  • B2.Gọi mDbHelper.createNote() sử dụng noteName làm title và “” làm body .
  • B3.Gọi phương thức fillData() để populate danh sách các notes – phương thức này sẽ được tạo trong bước tiếp theo.
  • Phương thức createNote() trông như sau:
private void createNote() { 

String noteName = “Note ” + mNoteNumber++;

mDbHelper.createNote(noteName, “”);

fillData();

}

XII.Step 12

  • Định nghĩa và cài đặt phương thức fillData().
  • Phương thức này sử dụng SimpleCursorAdapter và một Cursor để bind nó vào các field được cung cấp trong layout. Các field này định nghĩa các phần tử row của list (trong ví dụ này, field text1 trong layout “notes_row.xml”).
  • Để làm điều này bạn phải cung cấp một mapping từ field title trong Cursor trả về đến text1, mà được thực hiện bằng cách định nghĩa hai mảng: đầu tiên là một mảng string với danh sách các column cho map (trong ví dụ này là “title”, từ constant NotesDbAdapter.KEY_TITLE) và, thứ hai là một mảng int có chứa các tham chiếu đến các view để bạn bind dữ liệu vào (R.id.text1).
private void fillData() { 

// Get all of the notes from the database and create the item list

Cursor c = mDbHelper.fetchAllNotes();

startManagingCursor(c);

String[] from = new String[] { NotesDbAdapter.KEY_TITLE };

int[] to = new int[] { R.id.text1 };

// Now create an array adapter and set it to display using our row

SimpleCursorAdapter notes = new SimpleCursorAdapter(this,

R.layout.notes_row, c, from, to);

setListAdapter(notes);

}

Sau đây là những gì mà chúng ta đã thực hiện:

  • B1.Sau khi nhận được Cursor từ mDbHelper.fetchAllNotes(), chúng ta sử dụng phương thức startManagingCursor() của Activity để cho phép Android quan tâm đến lifecycle của Cursor thay vì chúng ta lo lắng về điều đó (chúng ta sẽ thảo luận vấn đề này trong bài tập 3, nhưng bây giờ chỉ biết rằng điều này cho phép Android làm một số công việc quản lý resource cho bạn).
  • B2.Sau đó chúng ta tạo ra một mảng string, trong đó khai báo các column mà chúng ta muốn (trong ví dụ này, chỉ có title), và một mảng int định nghĩa các View mà chúng ta muốn bind các column.
  • B3.Bước tiếp theo là khởi tạo SimpleCursorAdapter. Giống như nhiều lớp trong Android, SimpleCursorAdapter cần một Context để thực hiện các công việc của nó, vì vậy chúng ta gán “this” cho context (các lớp con của Activity thực thi Context). Chúng ta gán View “notes_row” đã tạo ra trước đó để làm chỗ chứa (receptable) cho dữ liệu, Cursor mà chúng ta vừa tạo ra và các mảng.

Trong tương lai, hãy nhớ rằng việc mapping từ các column và các resource được thực hiện bằng cách sử dụng thứ tự của hai mảng một cách tương ứng. Nếu bạn đã có nhiều column để bind, và nhiều View để bind chúng vào, bạn sẽ xác định chúng theo thứ tự, ví dụ bạn có thể sử dụng {NotesDbAdapter.KEY_TITLE, NotesDbAdapter.KEY_BODY} và {R.id.text1, R. id.text2} để bind hai field vào row (và cũng cần phải định nghĩa text2 trong notes_row.xml, cho body text). Đây là cách mà bạn có thể bind các field vào một row đơn (và có được một custom row layout).

XIII.Step 13

  • Chạy ứng dụng.

Link tham khảo: http://developer.android.com/resources/tutorials/notepad/notepad-ex1.html.