25 Februari 2012

Membuat Aplikasi Android: Notepadv1

Dalam latihan pembuatan aplikasi ini, kita akan membuat daftar catatan sederhana sehingga user bisa menambahkan catatan baru, namun tidak mengedit yang sudah ada. Pokok latihan terdiri dari:

  1. Dasar ListActivities serta membuat dan menangani menu option
  2. Bagaimana menggunakan SQLite untuk menyimpan catatan yang sudah dibuat.
  3. Bagaimana mengikat data dari sebuah cursor database ke sebuah ListView menggunakan sebuah SimpleCursorAdapter.
  4. Dasar layout layar, termasuk bagaimana mengatur layout sebuah list view, bagaimana menambahkan item ke menu activity, dan bagaimana activity tersebut menangani menu yang dipilih.

Tahap 1

Buatlah project baru di Eclipse, dengan data-data berikut ini:

  1. Project name: Notepadv1
  2. Build target: Android 2.2
  3. Application name: Notepadv1
  4. package name: org.pupil.notepadv1
  5. Create activity: Notepadv1Activity
  6. Minimum SDK: 8

Tahap 2

Setelah pembuatan project baru selesai, kita membutuhkan sebuah class untuk menyediakan enkapsulasi data akses ke sebuah database SQLite yang akan digunakan untuk menyimpan catatan kita dan mengijinkan kita mengupdate catatan tersebut. Pada package org.pupil.notepadv1 klik kanan dan pilih new -> java class. Kemudian isikan data-data berikut:

  1. Name: NotesDbAdapter
  2. Modifier: public
Setelah tulisan public class NotesDbAdapter {, tambahkan kode berikut ini:
 public static final String KEY_TITLE = "title";
 public static final String KEY_BODY = "body";
 public static final String KEY_ROWID = "_id";
 
 private static final String TAG = "NotesDbAdapter";
 private DatabaseHelper mDbHelper;
 private SQLiteDatabase mDb;
 
 /**statement sql untuk membuat database*/
 private static final String DATABASE_CREATE = 
   "create table notes (_id integer primary key autoincrement,"
   + "title text not null, body text not null);";
 
 private static final String DATABASE_NAME = "data";
 private static final String DATABASE_TABLE = "NOTES";
 private static final int DATABASE_VERSION = 2;
 
 private final Context mCtx;

Seperti terlihat di atas, terdapat beberapa deklarasi konstan yang akan digunakan sebagai field pada database. Juga terdapat sebuah string pembuatan database, yang mana digunakan untuk membuat schema database baru jika sebelumnya belum ada. Database yang kita pakai bernama data, dan memiliki sebuah table dengan nama notes, tabel tersebut memiliki tiga field, yaitu _id, title dan body. Nama field _id sengaja menggunakan underscore agar lebih memudahkan Android SDK dalam mengikuti perubahan keadaan. Field yang menjadi id biasanya harus di tentukan ketika melakukan query atau update database. Dua field lainnya digunakan untuk menyimpan data.

Tepat setelah baris terakhir dari kode di atas, tambahkan kode di bawah ini:

/* Konstruktor - menggunakan parameter bertipe Context agar database bisa dibuka atau dibuat*/
  public NotesDbAdapter(Context ctx) {
   this.mCtx = ctx;   
  }

Konstruktor class notesDbAdapter menggunakan parameter bertipe Context, agar class ini bisa berkomunikasi dengan aspek-aspek pada sistem operasi Android. Hal ini cukup umum untuk class-class yang perlu "bersentuhan" dengan sistem Android. Class activity menerapkan class Context, jadi biasanya kita cukup mengoperkan this dari Activity, ketika kita memerlukan sebuah Context.

Setelah baris terakhir, tambahkan kode berikut ini:

  private static class DatabaseHelper extends SQLiteOpenHelper {
   DatabaseHelper(Context konteks) {
    super(konteks, DATABASE_NAME, null, DATABASE_VERSION);
   }
   
   @Override
   public void onCreate(SQLiteDatabase db) {
    db.execSQL(DATABASE_CREATE);
   }
   
   @Override
   public void onUpgrade(SQLiteDatabase db, int versiLama, int versiBaru) {
    Log.w(TAG, "Upgrade database dari versi " + versiLama + " ke " + versiBaru + ", yang mana akan menghapus semua data lama");
    db.execSQL("DROP TABLE IF EXISTS notes");
    onCreate(db);
   }
  }
Dalam kode tersebut, kita membuat sebuah class dengan nama DatabaseHelper yang merupakan turunan dari class SQLiteOpenHelper

Kemudian tambahkan lagi kode berikut ini:

  /* membuka database catatan. jika tidak bisa dibuka, coba membuat
   * instansi database baru. Jika tidak bisa dibuat, lemparkan sebuah exception
   * untuk menandakan kegagalan
   * 
   * @return this (referensi diri sendiri, sehingga bisa diterapkan dalam inisialisasi berantai
   * 
   * @throws SQLException jika database tidak bisa dibuka atau di buat baru
   */
  public NotesDbAdapter open() throws SQLException {
   mDbHelper = new DatabaseHelper(mCtx);
   mDb = mDbHelper.getWritableDatabase();
   return this;
  }
Metode open() memanggil instansi dari DatabaseHelper. Instansi tersebut kemudian memanggil getWritableDatabase(), untuk menangani pembuatan dan pembukaan database untuk kita.

Berikutnya adalah metode close() yang digunakan untuk menutup database dan melepaskan sumberdaya yang terkait dengan koneksi ke database:

  public void close() {
   mDbHelper.close();
  }

Kode berikutnya:

  /* Membuat sebuah catatan baru menggunakan judul dan body yang sudah disediakan. Jika catatan
   * berhasil dibuat return rowID baru untuk catatan itu, jika gagal, return sebuah -1
   * untuk mengindikasikan kegagalan
   * @parameter title menjadi judul catatan
   * @parameter body menjadi body catatan
   * @return rowID atau -1 jika gagal
   */
  public long createNote(String title, String body) {
   ContentValues nilaiAwal = new ContentValues();
   nilaiAwal.put(KEY_TITLE, title);
   nilaiAwal.put(KEY_BODY, body);
   
   return mDb.insert(DATABASE_TABLE, null, nilaiAwal);
  }
metode createNote() mengambil string untuk judul dan body dari catatan baru, kemudian membuat catatan tersebut di database. Dengan asumsi bahwa catatan sukses dibuat, method ini juga return nilai baris _id untuk catatan baru yang dibuat.

Methode deleteNote() mengambil sebuah rowId milik sebuah catatan, dan menghapus catatan tersebut dari database. berikut ini adalah kodenya:

  /* Menghapus catatan berdasarkan rowID yang diberikan
   * 
   *  @parameter rowID Id catatan yang akan dihapus
   *  @return true jika berhasil di delete, false jika gagal
   */
  public boolean deleteNote(long rowId) {
   return mDb.delete(DATABASE_TABLE, KEY_ROWID + "=" + rowId, null) > 0;
  }

Berikut ini adalah methode fetchAllNotes() yang mengeluarkan sebuah query untuk me-return sebuah Cursor terhadap semua catatan di database.

  /* Return sebuah Cursor terhadap daftar semua catatan di database
   * 
   * @return Cursor terhadap semua catatan
   */
  public Cursor fetchAllNotes() {
   return mDb.query(DATABASE_TABLE, new String[] {KEY_ROWID, KEY_TITLE, KEY_BODY}, null, null, null, null, null);
  }
Pemanggilan metode query() digunakan untuk pemeriksaan dan pemahaman. Field pertama adalah nama table database untuk di query (dalam kasus ini DATABASE_TABLE adalah "notes"). Berikutnya adalah daftar kolom yang ingin dikembalikan, dalam kasus ini kita menginginkan kolom _id, title, dan body, jadi hal ini di terangkan di array string. Field yang tersisa, secara berurutan:selection,selectionArgs, groupBy, having dan orderby. Dengan mengeset semuanya menjadi null berarti kita menginginkan semua data, tanpa grouping, dan menggunakan order default.
Note: Sebuah Cursor dikembalikan lebih baik daripada sekumpulan baris. Dengan begini, Android bisa menggunakan sumberdaya dengan efisien. Bukan dengan menempatkan banyak data sekaligus ke memory, tapi Cursor akan mengambil dan melepaskan data sesuai kebutuhan. Yang mana akan sangat lebih efisien untuk table dengan banyak baris.

Metode fetchNote() serupa dengan fetchAllNotes() tapi hanya mengambil satu catatan dengan rowId yang kita tentukan.

  /*Return sebuah Curso yang ditempatkan pada catatan yang cocok dengan rowId yang diberikan
   * 
   * @parameter rowId id dari note yang ingin di ambil
   * @return Cursor ditempatkan pada catatan yang sesuai, jika ditemukan
   * @throws SQLException jika catatan tidak bisa ditemukan atau diambil
   */
  public Cursor fetchnote(long rowId) throws SQLException {
   Cursor mCursor = 
     mDb.query(true, DATABASE_TABLE, new String[] {KEY_ROWID, KEY_TITLE, KEY_BODY},
       KEY_ROWID + "=" + rowId, null, null, null, null, null);
   if (mCursor != null) {
    mCursor.moveToFirst();
   }
   return mCursor;
  }
Parameter pertama (set true) mengindikasikan bahwa kita tertarik dengan satu hasil yang berbeda. Parameter selection (parameter keempat) sudah ditentukan agar hanya mencari satu baris "where_id =" rowId yang kita operkan. jadi kita mendapat return sebuah Cursor pada satu baris.

Akhirnya metode updateNote() mengambil sebuah rowId, title, dan body, dan menggunakan sebuah instansi ContentValues untuk mengupdate catatan dari rowid yang diberikan.

  /* Update catatan menggunakan detail yang disediakan. Catatan yang diupdate
   * ditentukan menggunakan rowId, dan di ubah agar menggunakan nilai title dan body
   * yangdi operkan
   * 
   * @parameter rowId id catatan yang diupdate
   * @parameter title nilai baru untuk title catatan
   * @parameter body nilai baru body catatan
   * @return tru jika catatan sukses di update, false jika gagal
   */
  public boolean updateNote(long rowId, String title, String body) {
   ContentValues args = new ContentValues();
   args.put(KEY_TITLE, title);
   args.put(KEY_BODY, body);
   return mDb.update(DATABASE_TABLE, args, KEY_ROWID + "=" + rowId, null) > 0;
  }

Bila anda menggunakan Eclipse, tekan Ctrl-Shift-O untuk mengimport package yang diperlukan.

Tahap 3

Pada folder res/layout/ buatlah sebuah Android XML File dengan nama notepad_list.xml dan root elemet LinearLayout. Perhatikan gambar berikut:

Tahap 4

Kita memerlukan sebuah layout untuk menahan list yang kita miliki. Editlah file yang dibuat di tahap 3, sehingga terlihat seperti berikut ini:

<?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="match_parent"
    android:orientation="vertical" >
    <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>
  • Simbol @ pada string id dari tag ListView> dan TextView berarti bahwa XMl parser(pengurai) harus menguraikan (parsing) dan membuka seluruh id string dan menggunakan ID sumber daya.
  • Tag ListView dan TextView bisa dianggap sebagai dua alternatif view, hanya salah satu yang akan ditampilkan, tidak bisa keduanya sekaligus. listView akan digunakan untuk menampilkan catatan yang ada. Jika tidak ada catatan, akan ditampilkan TextView, yang mana memiliki nilai default "Belum ada catatan" didefinisikan sebagai sumberdaya string di res/values/strings.xml
  • ID list dan empty sudah disediakan oleh platform Android untuk kita, jadi, kita harus mengawali id dengan android: (misal, @android:id/list).
  • View dengan id empty otomatis digunakan ketika ListAdapter tidak memiliki data untuk ListView. ListAdapter secara default mengetahui untuk mencari nama ini. Jika ingin merubah nilai default, bisa digunakan setEmptyView(View) pada ListView.

Tahap 5

Untuk membuat daftar catatan di ListView, kita perlu mendefinisikan sebuah View untuk setiap baris:

  1. Buat Android XML File baru di res/layout dengan nama notes_row.xml.
  2. Edit file tersebut, menjadi seperti berikut:
    <?xml version="1.0" encoding="utf-8"?>
    <TextView 
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/text1" >    
    
    </TextView>
    View ini digunakan untuk setiap baris judul catatan. Dalam kasus ini kita membuat sebuah id baru text1. Tanda + setelah @ pada id string menunjukan bahwa id akan dibuat otomatis sebagai sumber daya jika belum ada, jadi kita mendefinisikan text1 on the fly dan kemudian menggunakannya.
  3. Save file tersebut.

Tahap 6

Pada tahap ini, kita akan mengedit file Notepadv1Activity.java sehingga menjadi seperti berikut ini:

package org.pupil.notepadv1;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

public class Notepadv1Activity extends Activity {
 private int mNoteNumber = 1;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
     return super.onCreateOptionsMenu(menu);
    }
    
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
     return super.onOptionsItemSelected(item);
    }
}
Kita akan menjadikan Notepadv1Activity.java menjadi sebuah list adapter dan menampilkan catatan kita, dan agar kita bisa menambah catatan baru.

Notepadv1Activity akan diturunkan dari sebuah subclass Activity yang disebut ListActivity, yang mana memiliki fungsi tambahan untuk mengakomodasi hal-hal yang ingin kita lakukan dengan list, misalnya: menampilkan jumlah item list yang berubah-ubah pada baris layar, pindah melalui item list, dan memilih item list.

Bila diperhatikan kode di atas, ada sebuah atribut private yang belum digunakan, yaitu mNoteNumber yang nantinya akan kita gunakan untuk membuat judul catatan bernomor.

Kita juga melakukan override terhadap 3 metode:

  • onCreate() dianggil ketika activity baru dimulai. Kita menggunakan metode ini untuk mengeset sumberdaya dan state untuk activity pada saat running.
  • onCreateOptionMenu() digunakan untuk menempatkan menu Activity. Akan ditampilkan ketika user menekan tombol menu, dan memiliki daftar option yang bisa dipilih (misalnya "create note").
  • onOptionItemSelected() melengkapi fitur menu, digunakan untuk menangani event yang dihasilkan dari menu (misalnya ketika user memilih "create menu").

Tahap 7

Ubah pewarisan Notepadv1Activity dari Activity menjadi ListActivity:

public class Notepadv1Activity extends listActivity
Setelah merubah kode, lakukan import package, dengan menekan Ctrl-Shift-O jika anada menggunakan Eclipse.

Tahap 8

Pada tahap ini, kita akan mengisi body dari metode onCreate(). Kita akan mengatur judul Activity (ditampilkan di atas layar), menggunakan layout notepad_list yang telah kita buat dengan XML, set up instansi NotesDbAdapter yang akan mengakses catatan, dan menempatkan daftar dengan judul yang ada:

  1. Pada metode onCreate(), panggil super.onCreate() dengan savedInstanceState sebagai parameter yang dioperkan.
  2. Panggil setContentView() dan operkan R.layout.notepad_list.
  3. Di bagian atas class, buat sebuah atribut private dengan nama mDbHelper dari class NotesDbAdapter.
  4. Kembali ke meotde onCreate(), konstruksi sebuah instansi baru NotesDbHelper dan tugaskan ke atribut mDbHelper (operkan this ke konstruktor untuk DBHelper).
  5. Panggil metode open() pada mDbHelper untuk membuka (atau membuat) database.
  6. Akhirnya, panggil sebuah metode baru bernama fillData(), yang mana akan mengambil data dan menempatkan listView menggunakan bantuan (metode ini belum didefinisikan).
Metode onCreate() seharusnya terlihat sebagai berikut:
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.notepad_list);
        mDbHelper = new NotesDbAdapter(this);
        mDbHelper.open();
        fillData();
    }
Jangan lupakan definisi mDbHelper (tepat dibawah definisi mNoteNumber)
private NotesDbAdapter mDbHelper;

Tahap 9

Di tahap ini, kita mengisi body dari metode onCreateOptionsMenu(). Sekarang kita akan membuat tombol "Add Item" yang bisa di akses dari tombol menu di perangkat. Kita akan menempatkannya di posisi pertama di menu.

  1. Pada sumber daya strings.xml (res/values), tambahkan sebuah string baru dengan nama "menu_insert" dengan nilai di set ke Add Item:
    <string name="menu_insert">Add Item</string>
    Kemudian simpan dan kembali ke Notepadv1Activity.
  2. Buat sebuah constant posisi menu di bagian paling atas class
    public static final int INSERT_ID = Menu.FIRST;
  3. Pada metode onCreateOptionsMenu(), ubah pemanggilan super sehingga kita menangkap pengembalian boolean sebagai result. Kita akan me-return nilai ini di akhir.
  4. Kemudian tambahkan item menu dengan menu.add().
Keseluruhan metode, seharusnya terlihat sebagai berikut:
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
     boolean result =  super.onCreateOptionsMenu(menu);
     menu.add(0, INSERT_ID, 0, R.String.menu_insert);
     return result;
    }
Argument yang dioperkan ke metode add() mengindikasikan: sebuah identifier group untuk menu ini (dalam kasus ini tidak ada), sebuah ID unik (didefinisikan di atas), pengurutan item (nol berarti tidak ada ketentuan), dan sumberdaya string yang digunakan pada item.

Tahap 10

Sekarang kita isi body dari metode onOptionsItemSelected(). Metode ini akan menangani item menu "Add Note". Ketika item menu ini dipilih, metode onOptionsItemSelected() akan dipanggil dengan item.getId() di set ke INSERT_ID (constant yang kita gunakan untuk identifikasi item menu). Kita bisa mendeteksinya, dan melakukan aksi yang sesuai:

  1. Pemanggilan metode super.onOptionsItemSelected(item) diletakkan di akhir metode ini, kita akan menangkap event terlebih dahulu.
  2. Tulis statement switch di item.getItemId().
    Pada case INSERT_ID, panggil sebuah metode baru, createnote(), dan return true, karena kita sudah menangani event ini dan tidak ingin menyebarkannya ke seluruh sistem.
  3. Return hasil dari metode superclass onOptionsItemSelected() di akhir.
Keseluruhan metode super.onOptionsItemSelected() terlihat sebagai berikut:
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
     switch (item.getItemId()) {
      case INSERT_ID:
       createNote();
       return true;
     }
     return super.onOptionsItemSelected(item);
    }

Tahap 11

Buat sebuah metode baru dengan nama createNote(). Pada aplikasi kita ini, createNote() secara sederhana digunakan untuk membuat catatan baru dengan judul yang ditugaskan kepadanya berdasarkan sebuah counter ("catatan 1", "catatan 2",....) dan dengan sebuah body yang kosong. Untuk sekarang ini kita tidak akan menambahkan fitur edit catatan, jadi sementara aplikasi kita hanya bisa membuat catatan baru dengan beberapa nilai default:

  1. Konstruksi nama catatan dengan "catatan" dan counter yang kita definisikan dalam class: String noteName = "Catatan " + mNoteNumber++
  2. Panggil mDbHelper.createNote() menggunakan noteName sebagai judul dan "" sebagai body.
  3. Panggil fillData() untuk menempatkan daftar catatan (kurang efisien namun simple), metode ini akan dibuat di tahap berikutnya.
Seluruh metode createNote() seharusnya terlihat sebagai berikut:
    private void createNote() {
     String noteName = "Catatan " + mNoteNumber++;
     mDbHelper.createNote(noteName, "");
     fillData();
    }

Tahap 12

Pada tahap ini kita akan mendefinisikan metode fillData(). Metode ini menggunakan SimpleCursorAdapter, yang mana mengambil sebuah database Cursor dan mengikatnya ke tempat yang disediakan di layout. Di tempat ini kita mendefinisikan elemen baris dari daftar kita (dalam kasus ini kita menggunakan field text1 pada layout notes_row.xml), dengan begini kita bisa dengan mudah menempatkan daftar dengan entry dari database.

Untuk melakukannya, kita harus menyediakan sebuah mapping dari field title pada Cursor yang dikembalikan, ke TextView text1, yang dilakukan dengan mendefinisikan du buah array: pertama sebuah array string dengan daftar kolom pemetaan berasal (dalam kasus ini hanya "title", dari constant NotesDbAdapter.KEY_TITLE) dan, yang kedua, sebuah array int berisi referensi ke tampilan yang akan kita ikat dengan data (TextView R.id.text1).

Berikut ini adalah metode fillData() yang seutuhnya:

    private void fillData() {
     // mengambil semua catatan di database dan membuat daftar item
     Cursor c = mDbHelper.fetchAllNotes();
     startManagingCursor(c);
     
     String[] from = new String[] {NotesDbAdapter.KEY_TITLE};
     int[] to = new int[] {R.id.text1};
     
     // Sekarang membuat sebuah adapter array  dan mengesetnya ke tampilan menggunakan baris kita
     SimpleCursorAdapter notes = 
       new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
     setListAdapter(notes);
    }

sejauh ini, yang kitalakukan dalah:

  1. Setelah menerima Cursor dari mDbHelper.fetchAllnotes(), kita gunakan sebuah metode dari class Activity startManagingCursor agar Android bisa menangani siklus hidup Cursor tanpa harus kita khawatirkan.
  2. Kemudian kita membuat sebuah array string yang di dalamnya kita deklarasikan kolom yang kita inginkan, dan sebuah array int yang mendefinisikan View yang akan kita ikat dengan kolom.
  3. Selanjutnya instansiasi SimpleCursorAdapter. Seperti halnya class-class lain di Android, class ini juga membutuhkan Context agar dapat berkerja, jadi kita operkan this untuk context. Kita operkan View notes_row yang kita buat sebagai wadah untuk menampung data, Cursor yang baru saja kita buat, dan array.

Di waktu yang akan datang, ingatlah bahwa pemetaan antara kolom from dan sumber daya to dilakukan menggunakan pengurutan dari masing-masing array. Jika kita memiliki kolom lagi untuk diikatkan, dan View lagi untuk diikatkan ke kolom, kita akan menentukannya secara berurutan, contohnya kita mungkin menggunakan {NotesDbAdapter.KET_TITLE, NotesDbAdapter.KEY_BODY} dan {R.id.text1, R.id.text2} untuk mengikat dua field ke dalam baris (dan kita harus mendefinisikan text2 di notes_row.xml, untuk text body). beginilah langkah untuk mengikat banyak field kedalam satu baris (dan mendapat layout baris yang disesuaikan).

Tahap 13

Semua proses sudah selesai dan project siap di-running. Berikut ini adalah screenshoot hasil yang saya peroleh:

Berikut ini adalah kode seutuhnya dari masing-masing file, anda bisa menggunakannya untuk koreksi.

  1. Notepadv1Activity.java
    package org.pupil.notepadv1;
    
    import android.app.ListActivity;
    import android.database.Cursor;
    import android.os.Bundle;
    import android.view.Menu;
    import android.view.MenuItem;
    import android.widget.SimpleCursorAdapter;
    
    public class Notepadv1Activity extends ListActivity {
     public static final int INSERT_ID = Menu.FIRST;
     private int mNoteNumber = 1;
     private NotesDbAdapter mDbHelper;
        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.notepad_list);
            mDbHelper = new NotesDbAdapter(this);
            mDbHelper.open();
            fillData();
        }
        
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
         boolean result =  super.onCreateOptionsMenu(menu);
         menu.add(0, INSERT_ID, 0, R.string.menu_insert);
         return result;
        }
        
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
         switch (item.getItemId()) {
          case INSERT_ID:
           createNote();
           return true;
         }
         return super.onOptionsItemSelected(item);
        }
        
        private void createNote() {
         String noteName = "Catatan " + mNoteNumber++;
         mDbHelper.createNote(noteName, "");
         fillData();
        }
        
        private void fillData() {
         // mengambil semua catatan di database dan membuat daftar item
         Cursor c = mDbHelper.fetchAllNotes();
         startManagingCursor(c);
         
         String[] from = new String[] {NotesDbAdapter.KEY_TITLE};
         int[] to = new int[] {R.id.text1};
         
         // Sekarang membuat sebuah adapter array  dan mengesetnya ke tampilan menggunakan baris kita
         SimpleCursorAdapter notes = 
           new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
         setListAdapter(notes);
        }
    }
  2. NotesDbAdapter.java:
    package org.pupil.notepadv1;
    
    import android.content.ContentValues;
    import android.content.Context;
    import android.database.Cursor;
    import android.database.SQLException;
    import android.database.sqlite.SQLiteDatabase;
    import android.database.sqlite.SQLiteOpenHelper;
    import android.util.Log;
    
    public class NotesDbAdapter {
         public static final String KEY_TITLE = "title";
      public static final String KEY_BODY = "body";
      public static final String KEY_ROWID = "_id";
      
      private static final String TAG = "NotesDbAdapter";
      private DatabaseHelper mDbHelper;
      private SQLiteDatabase mDb;
      
      /**statement SQL untuk membuat database*/
      private static final String DATABASE_CREATE = 
        "create table notes (_id integer primary key autoincrement,"
        + "title text not null, body text not null);";
      
      private static final String DATABASE_NAME = "data";
      private static final String DATABASE_TABLE = "NOTES";
      private static final int DATABASE_VERSION = 2;
      
      private final Context mCtx;
      
      /* Konstruktor - menggunakan parameter bertipe Context agar database bisa dibuka atau dibuat*/
      public NotesDbAdapter(Context ctx) {
       this.mCtx = ctx;   
      }
      
      private static class DatabaseHelper extends SQLiteOpenHelper {
       DatabaseHelper(Context konteks) {
        super(konteks, DATABASE_NAME, null, DATABASE_VERSION);
       }
       
       @Override
       public void onCreate(SQLiteDatabase db) {
        db.execSQL(DATABASE_CREATE);
       }
       
       @Override
       public void onUpgrade(SQLiteDatabase db, int versiLama, int versiBaru) {
        Log.w(TAG, "Upgrade database dari versi " + versiLama + " ke " + versiBaru + ", yang mana akan menghapus emua data lama");
        db.execSQL("DROP TABLE IF EXISTS notes");
        onCreate(db);
       }
      }
      
      public NotesDbAdapter open() throws SQLException {
       mDbHelper = new DatabaseHelper(mCtx);
       mDb = mDbHelper.getWritableDatabase();
       return this;
      }
      
      public void close() {
       mDbHelper.close();
      }
      
      /* Membuat sebuah catatan baru menggunakan judul dan body yang sudah disediakan. Jika catatan
       * berhasil dibuat return rowID baru untuk catatan itu, jika gagal, return sebuah -1
       * untuk mengindikasikan kegagalan
       * @parameter title menjadi judul catatan
       * @parameter body menjadi body catatan
       * @return rowID atau -1 jika gagal
       */
      public long createNote(String title, String body) {
       ContentValues nilaiAwal = new ContentValues();
       nilaiAwal.put(KEY_TITLE, title);
       nilaiAwal.put(KEY_BODY, body);
       
       return mDb.insert(DATABASE_TABLE, null, nilaiAwal);
      }
      
      /* Menghapus catatan berdasarkan rowID yang diberikan
       * 
       *  @parameter rowID Id catatan yang akan dihapus
       *  @return true jika berhasil di delete, false jika gagal
       */
      public boolean deleteNote(long rowId) {
       return mDb.delete(DATABASE_TABLE, KEY_ROWID + "=" + rowId, null) > 0;
      }
      
      /* Return sebuah Cursor terhadap daftar semua catatan di database
       * 
       * @return Cursor terhadap semua catatan
       */
      public Cursor fetchAllNotes() {
       return mDb.query(DATABASE_TABLE, new String[] {KEY_ROWID, KEY_TITLE, KEY_BODY}, null, null, null, null, null);
      }
      
      /*Return sebuah Curso yang ditempatkan pada catatan yang cocok dengan rowId yang diberikan
       * 
       * @parameter rowId id dari note yang ingin di ambil
       * @return Cursor ditempatkan pada catatan yang sesuai, jika ditemukan
       * @throws SQLException jika catatan tidak bisa ditemukan atau diambil
       */
      public Cursor fetchnote(long rowId) throws SQLException {
       Cursor mCursor = 
         mDb.query(true, DATABASE_TABLE, new String[] {KEY_ROWID, KEY_TITLE, KEY_BODY},
           KEY_ROWID + "=" + rowId, null, null, null, null, null);
       if (mCursor != null) {
        mCursor.moveToFirst();
       }
       return mCursor;
      }
      
      /* Update catatan menggunakan detail yang disediakan. Catatan yang diupdate
       * ditentukan menggunakan rowId, dan di ubah agar menggunakan niali title dan body
       * yangdi operkan
       * 
       * @parameter rowId id catatan yang diupdate
       * @parameter title nilai baru untuk title catatan
       * @parameter body nilai baru body catatan
       * @return tru jika catatan sukses di update, false jika gagal
       */
      public boolean updateNote(long rowId, String title, String body) {
       ContentValues args = new ContentValues();
       args.put(KEY_TITLE, title);
       args.put(KEY_BODY, body);
       return mDb.update(DATABASE_TABLE, args, KEY_ROWID + "=" + rowId, null) > 0;
      }
    }
  3. main.xml:
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical" >
    
        <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="@string/hello" />
    
    </LinearLayout>
  4. notepad_list.xml:
    <?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="match_parent"
        android:orientation="vertical" >
        <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>
  5. notes_row.xml:
    <?xml version="1.0" encoding="utf-8"?>
    <TextView xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/text1" >    
    
    </TextView>
  6. strings.xml:
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    
        <string name="hello">Hello World, Notepadv1Activity!</string>
        <string name="app_name">Notepadv1</string>
        <string name="menu_insert">Add Item</string>
        <string name="no_notes">""</string>
    
    </resources>


2 komentar:

  1. bro, mau nanya yang "main.xml" di kode seutuhnya dari masing-masing file itu file xml nya di res>layout ya?

    BalasHapus
    Balasan
    1. Ya bro. File main.xml yang disini adalah main.xml yang dihasilkan oleh eclipse saat membuat project baru.

      Hapus