Database untuk BukuSakuKu

...sebelum membaca tentang database untuk BukuSakuKu, silahkan melihat dahulu class untuk halaman depan_nya dan class untuk halaman isi_nya dengan klik disini.

Mengikuti nalar 'best practice' yang sering di kumandangkan oleh para ahli coding, maka class-class yang menyangkut database untuk BukuSakuKu ini akan saya bungkus dalam satu paket dengan nama com.vik_sintus.bukusakuku.database_nya
Menamakan paket adalah sangat tergantung struktur file masing-masing developer, di dunia java biasanya nama paket memakai cara PSV(period seperated values) atau tulisan yang di pisahkan oleh titik.

Seperti umumnya database, dia bekerja di belakang layar alias tidak berhadapan langsung dengan pengguna HP atau pengguna komputer. Yang kita lihat di layar HP adalah hanya yang indah-indah saja. Kita tak peduli mengapa yang indah-indah itu bisa muncul. Tapi para developer tentunya sangat peduli mengapa dan bagaimana suatu object tampil anggun di layar HP. Yang paling mendasar adalah komunikasi antara database dan aplikasinya, kalau komunikasinya tak benar maka segala sesuatu yang indah tak ada gunanya, karena tak akan muncul di layar HP. Itulah sebabnya mengapa saya menyimpan semua data yang menyangkut database di dalam satu paket.

Di Android, percakapan antara database dan aplikasi di lakukan oleh class yang bernama SQLiteOpenHelper.java dan sesuai namanya class ini kerjanya membantu membuka database.
Sedangkan SQLite adalah database yang di pakai di dalam semua android sistim. Android menggunakan SQLite karena ia serverless atau tak perlu server atau self contained sehingga enteng untuk alat-alat kecil seperti HP.
Mengikuti logika tersebut di atas maka class database untuk BukuSakuKu akan saya menamakan-nya DatabaseHelperNya.java yang akan merangkul class bawaan android yang bernama SQLiteOpenHelper.java
Berikut adalah isi dari DatabaseHelperNya.java
package com.vik_sintus.bukusakuku.database_nya;
/*
 * Copyright (C)2013 Vik Sintus Projects
 *
 * di larang pakai kode ini untuk kepentingan 
 * komersial tanpa ijin vik.sintus@gmail.com.
 * http://belajar-android-indonesia.blogspot.com
 *
 * Unless required by applicable law or agreed to in writing, 
 * this software is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
 * express or implied
 */
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DatabaseHelperNya extends SQLiteOpenHelper {

  private static final String NAMA_DATABASENYA = "bukusakukuDB.db";
  private static final int VERSI_DATABASENYA = 1;

  public DatabaseHelperNya(Context membuatDatabase) {
    super(membuatDatabase, NAMA_DATABASENYA, null, VERSI_DATABASENYA);
  }

  // Metode berikut akan di panggil saat membuat database
  @Override
  public void onCreate(SQLiteDatabase buatlahDatabaseNya) {
    ClassUntukTable.onCreate(buatlahDatabaseNya);
  }
 
  // Metode berikut di panggil ketika databasenya di upgrade,
  // tapi developer harus ubah angka versinya ke yang lebih tinggi yah
  // misalnya VERSI_DATABASENYA=2; dst
  @Override
  public void onUpgrade(SQLiteDatabase database_nya, int versiLama,
      int versiBaru) {
   ClassUntukTable.onUpgrade(database_nya, versiLama, versiBaru);
  }
} 

Kode di atas telah saya tulis menggunakan logika dan bahasa harian sehingga mudah untuk di pahami. Logikanya begini, deklarasikan nama class dengan menggandeng class SQLiteOpenHelper kemudian deklarasikan  NAMA_DATABASENYA dan VERSI_DATABASENYA (keduanya mirip variable) sampai disini analoginya sama seperti kita sudah punya kayu dan paku untuk membangun rumah(tapi dalam hal ini kita sedang membangun class java). Setelah alat-alatnya semua telah di siapkan maka di lanjutkan dengan  konstruksinya yang di mulai dari public DatabaseHelperNya(Context membuatDatabase) yaitu dalam konteks apa class ini di buat? tentunya kita mau membuatDatabase yang akan di kerjakan oleh metode onCreate() dan di dalam database akan ada table di ClassUntukTable.java

Masih di bawah paket com.vik_sintus.bukusakuku.database_nya buatkan class khusus untuk skema  table-nya. Tadi di DatabaseHelperNya telah di deklarasikan nama databasenya bukusakukuDB.db sekarang kita butuh sebuah table di dalam database tersebut. Oleh karena table mempunyai hubungan erat dengan database (hampir tak ada database tanpa table) maka class untuk table saya letakkan bersama class-class yang menyangkut database. Berikut adalah isi dari class ClassUntukTable.java

package com.vik_sintus.bukusakuku.database_nya;
/*
 * Copyright (C) 2013 Vik Sintus Projects
 *
 * Segala kelebihan dan kekurangan di 
 * luar tanggung jawab pembuat.
 * Di larang memakai kode ini untuk 
 * kepentingan komersial tanpa ijin.
 * Silahkan di pakai untuk kepentingan belajar.
 * vik.sintus@gmail.com
 *
 * Unless required by applicable law or agreed to in writing, 
 * this software is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
 * either express or implied.
 * 
 * 
 */
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

public class ClassUntukTable {
  // table untuk database bukusakukuDB.db
   public static final String TABLE_NYA = "bukusakuku";
   public static final String KOLOM_ID = "_id";
   public static final String KOLOM_JENIS = "jenis";
   public static final String KOLOM_JUDUL = "judul";
   public static final String KOLOM_ISI = "isi";

   // membuat table dengan SQL statement_nya
   private static final String BUATLAH_TABLE_NYA = "create table " 
       + TABLE_NYA
       + "(" 
       + KOLOM_ID + " integer primary key autoincrement, " 
       + KOLOM_JENIS + " text not null, " 
       + KOLOM_JUDUL + " text not null," 
       + KOLOM_ISI
       + " text not null" 
       + ");";

   public static void onCreate(SQLiteDatabase database_nya) {
     database_nya.execSQL(BUATLAH_TABLE_NYA);
   }

   public static void onUpgrade(SQLiteDatabase database_nya, int versiLama,
       int versiBaru) {
     Log.w(ClassUntukTable.class.getName(), "Database di upgrade dari versi "
         + versiLama + " ke " + versiBaru
         + ", dan akan menghapus semua data lama");
     database_nya.execSQL("DROP TABLE IF EXISTS " + TABLE_NYA);
     onCreate(database_nya);
   }
 }

Menempatkan class-class pada paketnya masing-masing sesuai kegunaan-nya adalah 'best practice' karena dengan cara itu ketika pembangunan aplikasi -nya berkembag atau kalau seandainya mau di rombak maka tak perlu membongkar semua aplikasi tapi yang di rombak hanyalah bagian-bagian kecilnya saja. Misalnya kalau mau rombak databasenya maka tinggal di pilih apanya yang mau di rombak, tambah jumlah kolom?, ganti tipe data? ganti versi aplikasi? apalagi ganti versi aplikasi sangat penting karena versi lama akan secara otomatis di hapus dengan versi baru sehingga tak perlu membebani resource HP pengguna

Masih ada satu class lagi yang di butuhkan untuk merampungkan pembangunan aplikasi ini yaitu class ContentProviderNya.java
Walau namanya ContentProvider tapi sesungguhnya ia tidak menyediakan kontent. dia bekerja mirip 'remote control' pada TV.
Stasiun TV adalah database dan layar TV adalah halaman aplikasi. Kalau tak suka nonton acara di salah satu stasiun TV janganlah ganti TVnya tapi ganti channel-nya saja, dengan memilih nomor sesuai stasiun TVnya di remote. Di content provider inilah tempat link ke database berada, sehingga kalau sumber datanya berubah misalnya dari 'local server' ke 'remote server' maka tak perlu rombak yang lain-lain tapi hanya merubah sedikit pada ContentProviderNya.
Walau nampak rumit melihat kodenya tapi sebenarnya proses kerja dari class ContentProviderNya.java hanya merangkul class aslinya yang telah ada di dalam sistim android bernama ContentProvider.java dengan kata kunci extends
Setelah deklarasikan semua variables yang di butuhkan oleh class ContentProviderNya.java, selebihnya hanya mengikrarkan semua metode bawaan class ContentProvider.java antara lain: onCreate(), query(), getType(), insert(), delete(), update().
Metode-metode bawaan tersebut di tandai dengan @Override maksudnya melangkahi proses kerja asli dari metode tersebut dan memasangnya dengan cara kerja yang sesuai kemauann kita sendiri, misalnya jikalau satu metode mempunyai default latarbelakang putih kita bisa merubahnya menjadi latarbelakang hitam tanpa mengubah sifat-sifat lain dari metode tersebut.

Demi 'best practice' maka antara saya dan aku telah sepakat untuk menempatkan class ContentProviderNya.java pada paket tersendiri yang saya beri nama package com.vik_sintus.bukusakuku.contentprovider_nya;
Untuk lebih jelasnya berikut adalah isi dari ContentProvider.java

package com.vik_sintus.bukusakuku.contentprovider_nya;
/*
 * Copyright (C)Vik Sintus Projects
 *
 * Segala kelebihan dan kekurangan di 
 * luar tanggung jawab pembuat.
 * Di larang memakai kode ini untuk 
 * kepentingan komersial tanpa ijin.
 * Silahkan di pakai untuk kepentingan belajar.
 * vik.sintus@gmail.com
 *
 * Unless required by applicable law or agreed to in writing, 
 * this software is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
 * either express or implied.
 * 
 * 
 */
import java.util.Arrays;
import java.util.HashSet;

import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;

import com.vik_sintus.bukusakuku.database_nya.DatabaseHelperNya;
import com.vik_sintus.bukusakuku.database_nya.ClassUntukTable;


public class ContentProviderNya extends ContentProvider {

  // databasenya
  private DatabaseHelperNya databasenya;

  // di gunakan untuk mencocokan data dan
  // mengIdentifikasikan sumber datanya
  private static final int MENGISI_DATA = 10;
  private static final int IDENTITAS_DATA = 20;

  private static final String OTORITAS = "com.vik_sintus.bukusakuku.contentprovider_nya";

  private static final String BASE_PATH_NYA = "meMasukanData";
  public static final Uri CONTENT_URI_NYA = Uri.parse("content://" + OTORITAS
      + "/" + BASE_PATH_NYA);

  public static final String TIPE_CONTENT_NYA = ContentResolver.CURSOR_DIR_BASE_TYPE
      + "/meMasukanData";
  public static final String TIPE_CONTENT_ITEM_NYA = ContentResolver.CURSOR_ITEM_BASE_TYPE
      + "/bukusakuku";

  private static final UriMatcher cocokanURInya = new UriMatcher(UriMatcher.NO_MATCH);
  static {
    cocokanURInya.addURI(OTORITAS, BASE_PATH_NYA, MENGISI_DATA);
    cocokanURInya.addURI(OTORITAS, BASE_PATH_NYA + "/#", IDENTITAS_DATA);
  }
 //override lalu ambil context dari DatabaseHelperNya
  @Override
  public boolean onCreate() {
    databasenya = new DatabaseHelperNya(getContext());
    return false;
  }

  @Override
  public Cursor query(Uri uriNya, String[] apaYangMauDiQuery, String selection,
      String[] selectionArgs, String sortOrder) {

    // pakai SQLiteQueryBuilder ketimbang metode query() 
    SQLiteQueryBuilder queryBuilderNya = new SQLiteQueryBuilder();

    // priksa dulu apakah yang di minta/cari adalah sebuah kolom yang
    // tak eksis 
    priksaKolom(apaYangMauDiQuery);

    // pasang table_nya
    queryBuilderNya.setTables(ClassUntukTable.TABLE_NYA);

    int tipeURInya = cocokanURInya.match(uriNya);
    switch (tipeURInya) {
    case MENGISI_DATA:
      break;
    case IDENTITAS_DATA:
      // tambahkan identitas_data pada query aslinya
      queryBuilderNya.appendWhere(ClassUntukTable.KOLOM_ID + "="
          + uriNya.getLastPathSegment());
      break;
    default:
      throw new IllegalArgumentException("URI_nya tak di kenal: " + uriNya);
    }

    SQLiteDatabase db = databasenya.getWritableDatabase();
    Cursor cursor = queryBuilderNya.query(db, apaYangMauDiQuery, selection,
        selectionArgs, null, null, sortOrder);
    // pastikan semua listeners untuk selalu siap(mendengar)
    cursor.setNotificationUri(getContext().getContentResolver(), uriNya);

    return cursor;
  }

  @Override
  public String getType(Uri uri) {
    return null;
  }

  @Override
  public Uri insert(Uri uriNya, ContentValues dataYangMauDiInsert) {
    int tipeURInya = cocokanURInya.match(uriNya);
    SQLiteDatabase sqlDB = databasenya.getWritableDatabase();
    long id = 0;
    switch (tipeURInya) {
    case MENGISI_DATA:
      id = sqlDB.insert(ClassUntukTable.TABLE_NYA, null, dataYangMauDiInsert);
      break;
    default:
      throw new IllegalArgumentException("URI tak di kenal: " + uriNya);
    }
    getContext().getContentResolver().notifyChange(uriNya, null);
    return Uri.parse(BASE_PATH_NYA + "/" + id);
  }

  @Override
  public int delete(Uri uriNya, String dataYangDiPilih, String[] dataYangAda) {
    int tipeURInya = cocokanURInya.match(uriNya);
    SQLiteDatabase sqlDB = databasenya.getWritableDatabase();
    int barisDataYangTerhapus = 0;
    switch (tipeURInya) {
    case MENGISI_DATA:
      barisDataYangTerhapus = sqlDB.delete(ClassUntukTable.TABLE_NYA, dataYangDiPilih,
          dataYangAda);
      break;
    case IDENTITAS_DATA:
      String id = uriNya.getLastPathSegment();
      if (TextUtils.isEmpty(dataYangDiPilih)) {
        barisDataYangTerhapus = sqlDB.delete(ClassUntukTable.TABLE_NYA,
          ClassUntukTable.KOLOM_ID + "=" + id, 
            null);
      } else {
        barisDataYangTerhapus = sqlDB.delete(ClassUntukTable.TABLE_NYA,
          ClassUntukTable.KOLOM_ID + "=" + id 
            + " and " + dataYangDiPilih,
            dataYangAda);
      }
      break;
    default:
      throw new IllegalArgumentException("URInya tak di kenal: " + uriNya);
    }
    getContext().getContentResolver().notifyChange(uriNya, null);
    return barisDataYangTerhapus;
  }

  @Override
  public int update(Uri uriNya, ContentValues diIsiDenganValue, String dataYangDiPilih,
      String[] dataYangAda) {

    int uriType = cocokanURInya.match(uriNya);
    SQLiteDatabase sqlDB = databasenya.getWritableDatabase();
    int rowsUpdated = 0;
    switch (uriType) {
    case MENGISI_DATA:
      rowsUpdated = sqlDB.update(ClassUntukTable.TABLE_NYA, 
          diIsiDenganValue, 
          dataYangDiPilih,
          dataYangAda);
      break;
    case IDENTITAS_DATA:
      String id = uriNya.getLastPathSegment();
      if (TextUtils.isEmpty(dataYangDiPilih)) {
        rowsUpdated = sqlDB.update(ClassUntukTable.TABLE_NYA, 
            diIsiDenganValue,
            ClassUntukTable.KOLOM_ID + "=" + id, 
            null);
      } else {
        rowsUpdated = sqlDB.update(ClassUntukTable.TABLE_NYA, 
            diIsiDenganValue,
            ClassUntukTable.KOLOM_ID + "=" + id 
            + " and " 
            + dataYangDiPilih,
            dataYangAda);
      }
      break;
    default:
      throw new IllegalArgumentException("URI_nya Tak di kenal: " + uriNya);
    }
    getContext().getContentResolver().notifyChange(uriNya, null);
    return rowsUpdated;
  }

  private void priksaKolom(String[] kolomYangMauDiLihat) {
    String[] dataYangTelahAda = { ClassUntukTable.KOLOM_JENIS,
      ClassUntukTable.KOLOM_JUDUL, ClassUntukTable.KOLOM_ISI,
      ClassUntukTable.KOLOM_ID };
    if (kolomYangMauDiLihat != null) {
      HashSet<String> kolomYangDiCari = new HashSet<String>(Arrays.asList(kolomYangMauDiLihat));
      HashSet<String> kolomYangTersedia = new HashSet<String>(Arrays.asList(dataYangTelahAda));
      // priksa dulu apakah kolom yang di maksud ada atau tidak
      if (!kolomYangTersedia.containsAll(kolomYangDiCari)) {
        throw new IllegalArgumentException("Tak ada kolom dgn nama tsb");
      }
    }
  }

}

Dengan demikian maka semua class java telah selesai di buat yang semuanya berjumlah 5 buah. Kelima file java tersebut seharusnya tersusun di dalam file struktur seperti terlihat pada gambar berikut:


 Selanjutnya kita akan melengkapi aplikasi ini dengan 7 buah file xml dan 2 buah logo yang harus tersusun seperti terlihat pada gambar berikut:

Walau terlihat ada 4 buah logo tetapi saya hanya memakai(memanggil 2 buah)(hanya karena saya malas untuk menghapus logo yang tak terpakai)

Isi dari ke 7 file xml tersebut di atas adalah sbb:

tampil_perbaris.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="wrap_content" >

    <ImageView
        android:id="@+id/icon"
        android:layout_width="30dp"
        android:contentDescription="@string/logo"
        android:layout_height="24dp"
        android:layout_marginLeft="4dp"
        android:layout_marginRight="8dp"
        android:layout_marginTop="8dp"
        android:src="@drawable/logo_kecil" />
    

    <TextView
        android:id="@+id/tampil_hanya_judul"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="6dp"
        android:lines="1"
        android:text="@+id/untuk_baris_judul"
        android:textSize="24sp" />
   

</LinearLayout>


tampilan_bagian_isi.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" >

    <Spinner
        android:id="@+id/jenisnya"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:entries="@array/priorities" >
    </Spinner>

    <LinearLayout
        android:id="@+id/LinearLayout01"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <EditText
            android:id="@+id/isilah_judulnya"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:hint="@string/isilah_judulnya"
            android:imeOptions="actionNext" >
        </EditText>
    </LinearLayout>

    <EditText
        android:id="@+id/isilah_isinya"
        android:layout_width="match_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        android:gravity="top"
        android:hint="@string/isilah_isinya"
        android:imeOptions="actionNext" >
    </EditText>

    <Button
        android:id="@+id/tombol_simpan"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/simpanlah" >
    </Button>

</LinearLayout>


tampilan_depan.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="match_parent"
        android:layout_height="wrap_content" >
    </ListView>

    <TextView
        android:id="@android:id/empty"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/tak_ada_isi" />

</LinearLayout>


untuk_menu_utama.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:id="@+id/menulis_data"
        android:showAsAction="always"
        android:title="Tulis">
    </item>

</menu>


priority.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string-array name="priorities">
        <item>Segera</item>
        <item>Ingat</item>
    </string-array>

</resources>


strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string name="hello">BukuSakuKu</string>
  <string name="nama_aplikasi">BukuSakuKu</string>
  <string name="tak_ada_isi">Tak ada isi</string>
  <string name="tambahkan">Tambahkan</string>
  <string name="hapus_judul">Hapus</string>
  <string name="judulnya">judul</string>
  <string name="hapus_isinya">Hapus</string>
  <string name="isilah_judulnya">Judulnya</string>
  <string name="isilah_isinya">Isinya</string>
  <string name="simpanlah">Simpan</string>
  <string name="logo">BukuSakuKu</string>
</resources>


styles.xml
<resources>

    <style name="AppTheme" parent="android:Theme.Light" />

</resources> 


Semua file yang di butuhkan telah di pasang,.. silahkan di kembangkan dan kalau perlu di upload ke market

Buku Saku untuk HP Android

Pada jaman dahulu kala, sebelum ada HP, yang ada di saku celana atau saku baju di hampir setiap orang yang non-buta huruf adalah hanya sebuah buku kecil yang bernama 'buku saku' atau bahasa kerén-nya 'buku notes' atau bahasa kerén-nya lagi 'notes book'.
Saya mau mengenang masa lalu sekalian sambil belajar dengan membangun sebuah aplikasi untuk di install ke HP android agar dapat di pakai bagaikan buku saku. Di sini saya tekankan bahwa bukan aplikasinya yang di kedepankan tapi cara membuat aplikasi yang saya mau tularkan. Siapa tahu dengan mengetahui aplikasi sederhana seperti ini, akan dapat memompa semangat dan melahirkan ide untuk membuat aplikasi yang lebih canggih dan di minat pasar. Skalian juga belajar CRUD(Create, Retrieve, Update, Delete) di android.
Sudah cukup banyak aplikasi yang menyerupai buku saku in di pasaran tapi semuanya cukup berat untuk HP, misalnya aplikasi Notepad bisa saja di pakai sebagai buku saku karena bisa juga di pakai untuk membuat catatan kecil tapi setiap kali kita menyimpan data dari notepad di HP maka selalu akan memakan banyak ruang simpan karena harus menyimpan sehalaman notepad walau isinya hanya sebaris tulisan.

Buku Saku yang saya bangun ini hanya akan memiliki judul dan isinya (lihat Gambar 2 dan Gambar 3). Yang akan tampak di halaman depan adalah judulnya saja (lihat Gambar 4), dan ketika di klik pada judul maka akan terlihat isinya (lihat Gambar 3).
Untuk dapat masukan data ke dalam Buku Saku ini, maka di pojok kanan atas layar HP akan di bubuhkan sebuah menu(tombol) dengan nama "Tulis" yang kalau di klik atau di sentuh maka akan membuka halaman baru(kertas baru) yang siap untuk di tulis (lihat Gambar 1). Perhatikan gambar-gambar berikut:
Gambar 1
Gambar 2
Gambar 3
Gambar 4
Gambar 5

Dari segi UI(user interface) Buku Saku ini akan memiliki kriteria sbb:

  1. Ada menu atau tombol untuk membuka halaman baru
  2. Ada pilihan (jenis) data misalnya 'segera' atau hanya 'untuk di ingat' (lihat Gambar 3)
  3. Ada bagian judul (lihat Gambar 2)
  4. Ada bagian isi (lihat Gambar 2)
  5. Ada menu atau tombol untuk bisa simpan data (lihat Gambar 3)
  6. Ada menu atau tombol untuk bisa menghapus data (lihat Gambar 5)
Berdasarkan kriteria-kriteria tersebut, maka halaman depan atau halaman pertama yang muncul di layar HP tentunya adalah sebuah kertas kosong atau halaman kosong atau layar kosong, persis seperti ketika kita membuka buku saku benaran yang belum ada isinya (lihat Gambar 1).
Kertas kosong tersebut akan di representasikan dengan class java tersendiri dan secara exclusive saya menamakan classnya BStampilanDepan.java

Class BStampilanDepan.java adalah logika java untuk halaman depan BukuSakuKu



Logika menamakan class tergantung selera masing-masing developer. BS pada nama class diatas adalah singkatan dari 'buku saku'. hal itu hanya untuk logika pribadi saya saja.

Walau Halaman depan  Buku Saku ini akan nampak kosong, tetapi class BSbagianDepan.java yang merepresentasikan-nya akan di kode-kan dengan cukup padat karena kedepan-nya halaman depan ini tak akan kosong lagi. Halaman depan-nya akan di isi dengan judul-judul dari tulisan yang akan menempatkan diri tampil berjajar baris demi baris, sehingga dengan demikian dapat memudahkan pengguna untuk scrolling mencari tulisan-nya secara cepat.

Oleh karena saya ingin tulisan-nya tampil berjajar baris demi baris maka class BSbagianDepan.java akan menggandeng class ListActivity.java dengan kata kunci extends karena sesuai namanya List = (daftar)perbaris.
Halaman depan juga harus mempunyai kemampuan untuk me-loading(menyimpan dan membuka) data. Untuk itu di perlukan class LoaderManager.java dengan kata kunci implements
Sesuai namanya class LoaderManager.java  adalah bekerja me-load yaitu upload(simpan) dan download(tunjukan data ke halaman). Class ListActivity dan class LoaderManager sudah ada dalam operasi sistim android sehingga kita tinggal memakainya saja dengan menggunakan kata kunci yang telah saya sebutkan di atas.

Saya berasumsi bahwa anda membaca tulisan ini karena anda sudah kenal proses kerja android di eclipse. Berikut adalah isi dari class BSbagianDepan.java yang akan merenda halaman depan aplikasi ini yang saya kasih nama lengkapnya BukuSakuKu

package com.vik_sintus.bukusakuku;
/**
 * Copyright (C) 2013 Vik Sintus Projects
 *
 * Segala kelebihan dan kekurangan di luar tanggung jawab pembuat.
 * Di larang memakai kode ini untuk kepentingan komersial tanpa ijin.
 * Silahkan di pakai untuk kepentingan belajar.
 * vik.sintus@gmail.com
 * http://belajar-android-indonesia.blogspot.com
 * Unless required by applicable law or agreed to in writing, this software
 * is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * 
 * 
 */
import android.app.ListActivity;
import android.app.LoaderManager;
import android.content.CursorLoader;
import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import com.vik_sintus.bukusakuku.contentprovider_nya.ContentProviderNya;
import com.vik_sintus.bukusakuku.database_nya.ClassUntukTable;

/*
 * class ini untuk menunjukan data yang telah ada di
 * BukuSakuKu
 * 
 * dan masukan data baru dengan menekan tombol 'Tulis' 
 * Untuk menghapus data yang tak di butuhkan lagi silahkan
 * long press atau sentuh agak lama pada nama data yang 
 * mau di hapus sampai muncul tulisan 'Hapus'
 */

public class BStampilanDepan extends ListActivity implements
    LoaderManager.LoaderCallbacks<Cursor> {
  private static final int ID_HAPUS_DATA = Menu.FIRST + 1;
  // private Cursor cursor;
  private SimpleCursorAdapter adapter_nya;

  
/** di panggil saat membuat activity_nya. */

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.tampilan_depan);
    this.getListView().setDividerHeight(2);
    tampilkanData();// tampil data darimana? lihat private void
    registerForContextMenu(getListView());
  }

  // buat menu 'Tulis' di pojok kanan atas
  @Override
  public boolean onCreateOptionsMenu(Menu menuPembuka) {
    MenuInflater ketikaKlikPadaMenu = getMenuInflater();
    ketikaKlikPadaMenu.inflate(R.menu.untuk_menu_utama, menuPembuka);
    return true;
  }
 
  // ketika membuka halaman baru(kertas kosong) untuk
  // masukanData
  @Override
  public boolean onOptionsItemSelected(MenuItem ketikaMauTulis) {
    switch (ketikaMauTulis.getItemId()) {
    case R.id.menulis_data:
      masukanData();//masukan data yang bagaimana? lihat private void
      return true;
    }
    return super.onOptionsItemSelected(ketikaMauTulis);
  }

  @Override
  public boolean onContextItemSelected(MenuItem itemYangDiKlik) {
    switch (itemYangDiKlik.getItemId()) {
    case ID_HAPUS_DATA:
      AdapterContextMenuInfo informasiMenu = (AdapterContextMenuInfo) itemYangDiKlik
          .getMenuInfo();
      Uri resource_nya = Uri.parse(ContentProviderNya.CONTENT_URI_NYA + "/"
          + informasiMenu.id);
      getContentResolver().delete(resource_nya, null, null);
      tampilkanData();
      return true;
    }
    return super.onContextItemSelected(itemYangDiKlik);
  }
  // simpan data ke bagian isi
  private void masukanData() {
    Intent simpanData = new Intent(this, BSbagianIsi.class);
    startActivity(simpanData);
  }

  // halaman data (yaitu activity kedua) atau halaman bagian
  // dalam atau isi dari buku saku (BSbagianIsi.java) akan terbuka saat mau
  // mengisi data baru atau mengubah data lama
  @Override
  protected void onListItemClick(ListView barisanData, View tampilanData, int letakData, long id) {
    super.onListItemClick(barisanData, tampilanData, letakData, id);
    Intent intent_nya = new Intent(this, BSbagianIsi.class);
    Uri identifikasiSumberData = Uri.parse(ContentProviderNya.CONTENT_URI_NYA + "/" + id);
    intent_nya.putExtra(ContentProviderNya.TIPE_CONTENT_ITEM_NYA, identifikasiSumberData);

    startActivity(intent_nya);
  }

  

  private void tampilkanData() {
    // data mana saja dari database (projection) yang
    // harus di sertakan dalam kolom _id utk tampil di halaman depan
 // sehingga hanya judulnya saja yang tampil di halaman depan
    String[] dataDari = new String[] { ClassUntukTable.KOLOM_JUDUL };
    int[] keTempatTampilNya = new int[] { R.id.tampil_hanya_judul };
    // di load atau di pasang ke halaman baris demi baris
    getLoaderManager().initLoader(0, null, this);
    adapter_nya = new SimpleCursorAdapter(this, R.layout.tampil_perbaris, null, dataDari,
        keTempatTampilNya, 0);

    setListAdapter(adapter_nya);
  }
   // berikut ketika sentuh(long press) pada judul sehingga
   // keluar tombol 'Hapus' agar bisa menghapus data yang 
   // telah di sentuh
  @Override
  public void onCreateContextMenu(ContextMenu menu, View v,
      ContextMenuInfo menuInfo) {
    super.onCreateContextMenu(menu, v, menuInfo);
    menu.add(0, ID_HAPUS_DATA, 0, R.string.hapus_judul);
  }

  // membuat sebuah loader baru setelah memanggil metode initLoader ()
  // sehingga me-load lagi judul- judul lain yang tak ingin di hapus dan 
  // tertata kembali agar ruangan bekas tempat judul yang telah di hapus tak
  // bolong(kosong)
  @Override
  public Loader<Cursor> onCreateLoader(int id, Bundle semuaDataYangMauDiLoad) {
    String[] dataManaYangMauDiLoad = { ClassUntukTable.KOLOM_ID, ClassUntukTable.KOLOM_JUDUL };
    CursorLoader cursorLoader = new CursorLoader(this,
        ContentProviderNya.CONTENT_URI_NYA, dataManaYangMauDiLoad, null, null, null);
    return cursorLoader;
  }

  @Override
  public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    adapter_nya.swapCursor(data);
  }

  @Override
  public void onLoaderReset(Loader<Cursor> loader) {
    // datanya tak ada lagi, hapus semua yang berhubungan
 // dengan data tsb
    adapter_nya.swapCursor(null);
  }

}
Perlu saya ingatkan lagi bahwa kode class BStampilanDepan.java tersebut di atas adalah logika internal untuk halaman depan BukuSaku. Ia membutuhkan user interface(UI) agar dapat di lihat dan di sentuh atau di pencet oleh manusia. Berikut adalah beberapa UI-nya.

⇒Lihat pada metode onCrate(); disana di minta bagaimana tampang depan dari aplikasi ini. Tampang depan untuk buku saku yang masih baru, tentunya masih kosong bukan? dan nanti kalau sudah ada isinya, saya ingin isinya itu akan tampil baris demi baris. Untuk menghasilkan itu maka berikut adalah file xml yang saya beri nama tampilan_depan.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="match_parent"
        android:layout_height="wrap_content" >
    </ListView>

    <TextView
        android:id="@android:id/empty"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/tak_ada_isi" />

</LinearLayout>
android:id="@android:id/list" = agar nanti kalau sudah ada catatan di buku sakunya maka akan tampil berjajar dan berbaris.
android:id="@android:id/empty" = kosong ketika belum ada isinya

⇒Lihat pada metode public boolean onCreateOptionsMenu disana minta tampilan menu utama aplikasi ini sehingga dengan demikian saya membuat file xml dengan nama untuk_menu_utama.xml yang terlihat sbb:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:id="@+id/menulis_data"
        android:showAsAction="always"
        android:title="Tulis">
    </item>

</menu>
Menu utamanya hanya berisi kata "Tulis" di pojok kanan atas layar HP

Selain kedua halaman xml tsb di atas class BStampilanDepan.java juga membutuhkan pengaturan file saat mengisi data.
⇒Lihat metode private void isilahData();
Perbaris adalah yang paling tepat untuk layar kecil seperti HP sehingga dengan demikian saya membuat file dengan nama tampil_perbaris.xml yang isinya terlihat sbb:

<?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="wrap_content" >

    <ImageView
        android:id="@+id/icon"
        android:layout_width="30dp"
        android:contentDescription="@string/logo"
        android:layout_height="24dp"
        android:layout_marginLeft="4dp"
        android:layout_marginRight="8dp"
        android:layout_marginTop="8dp"
        android:src="@drawable/logo_kecil" />
    

    <TextView
        android:id="@+id/tampil_hanya_judul"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="6dp"
        android:lines="1"
        android:text="@+id/untuk_baris_judul"
        android:textSize="24dp" />
   

</LinearLayout>

Agar HP tahu halaman mana yang muncul ketika menghidupkan aplikasi, maka class BStampilanDepan ini akan di tampilkan di halaman AndroidManifest.xml nanti saya akan bicarakan AndriodManifest sekarang mari kita bicarakan bagaimana membuat logika java untuk halam isi yaitu halaman yang akan menyimpan tulisan di buku saku secara detail seperti terlihat pada Gambar 3. Secara exclusive saya menamakan class untuk logika halaman isi adalah BSbagianIsi.java

Class BSbagianIsi.java adalah logika java untuk halaman yang menampung isi dari BukSakuKu


Di dalam halaman isi akan tampak segala sesuatu yang menyangkut isi, termasuk judul juga akan nampak disana. Selain itu di sana juga akan ada tombol 'Simpan' dan tombol 'Hapus'. Tadi juga saya singgung sedikit tentang jenis catatan yang mau di masukan kedalam buku saku, misalnya apakah catatan-nya bersifat segera, atau hanya untuk di ingat saja. Sebenarnya hal itu tak begitu penting tapi saya mau melebarkan sayap dalam hal developing aplikasi.
Dengan demikian maka yang akan tampil di bagian utama halaman isi adalah 3 buah objects antara lain

  1.  Bagian jenis tulisan akan menggunakan object 'Spinner'
  2.  Bagian judul akan menggunakan object 'EditText'
  3.  Bagian isi juga menggunakan object 'EditText'

Secara sederhana Spinner = dropdown menu dalam HTML sedangkan EditText = text area
Berikut adalah isi dari class BSbagianIsi.java


package com.vik_sintus.bukusakuku;

import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Toast;
import com.vik_sintus.bukusakuku.contentprovider_nya.ContentProviderNya;
import com.vik_sintus.bukusakuku.database_nya.ClassUntukTable;

/*
 * untuk isi BukuSaku
 * masukan data dan hapus data 
 */
public class BSbagianIsi extends Activity {
  private Spinner utkJenis;
  private EditText utkJudul;
  private EditText utkIsi;

  private Uri identifikasiData;

  @Override
  protected void onCreate(Bundle merendaData) {
    super.onCreate(merendaData);
    setContentView(R.layout.tampilan_bagian_isi);

    utkJenis = (Spinner) findViewById(R.id.jenisnya);
    utkJudul = (EditText) findViewById(R.id.isilah_judulnya);
    utkIsi = (EditText) findViewById(R.id.isilah_isinya);
    Button tombolSimpan = (Button) findViewById(R.id.tombol_simpan);

    Bundle dataTambahan = getIntent().getExtras();

    // lihat dan identifikasikan data pada 
    // instance yg telah di simpan sebelumnya
    identifikasiData = (merendaData == null) ? null : (Uri) merendaData
        .getParcelable(ContentProviderNya.TIPE_CONTENT_ITEM_NYA);

    // atau ambil dari activity yang lain
    if (dataTambahan != null) {
      identifikasiData = dataTambahan
          .getParcelable(ContentProviderNya.TIPE_CONTENT_ITEM_NYA);

      isilahData(identifikasiData);
    }

    tombolSimpan.setOnClickListener(new View.OnClickListener() {
      public void onClick(View view) {
        if (TextUtils.isEmpty(utkJudul.getText().toString())) {
          tinggalkanPesanPakaiToast();
        } else {
          setResult(RESULT_OK);
          finish();
        }
      }

    });
  }

  private void isilahData(Uri perkenalkanData) {
    String[] kolomYangDisertakan = { ClassUntukTable.KOLOM_JUDUL,
      ClassUntukTable.KOLOM_ISI, ClassUntukTable.KOLOM_JENIS };
    Cursor cursorNya = getContentResolver().query(perkenalkanData, kolomYangDisertakan, null, null,
        null);
    if (cursorNya != null) {
      cursorNya.moveToFirst();
      String category = cursorNya.getString(cursorNya
          .getColumnIndexOrThrow(ClassUntukTable.KOLOM_JENIS));

      for (int i = 0; i < utkJenis.getCount(); i++) {

        String s = (String) utkJenis.getItemAtPosition(i);
        if (s.equalsIgnoreCase(category)) {
          utkJenis.setSelection(i);
        }
      }

      utkJudul.setText(cursorNya.getString(cursorNya
          .getColumnIndexOrThrow(ClassUntukTable.KOLOM_JUDUL)));
      utkIsi.setText(cursorNya.getString(cursorNya
          .getColumnIndexOrThrow(ClassUntukTable.KOLOM_ISI)));

      // cursornya harus selalu di tutup
      cursorNya.close();
    }
  }

  protected void onSaveInstanceState(Bundle ketikaDiMatikan) {
    super.onSaveInstanceState(ketikaDiMatikan);
    diSimpan();
    ketikaDiMatikan.putParcelable(ContentProviderNya.TIPE_CONTENT_ITEM_NYA, identifikasiData);
  }

  @Override
  protected void onPause() {
    super.onPause();
    diSimpan();
  }

  private void diSimpan() {
    String jenisnya = (String) utkJenis.getSelectedItem();
    String judulnya = utkJudul.getText().toString();
    String isinya = utkIsi.getText().toString();

    // hanya bisa di simpan apabila ada judul dan isinya 
    if (isinya.length() == 0 && judulnya.length() == 0) {
      return;
    }

    ContentValues simpanTulisan = new ContentValues();
    simpanTulisan.put(ClassUntukTable.KOLOM_JENIS, jenisnya);
    simpanTulisan.put(ClassUntukTable.KOLOM_JUDUL, judulnya);
    simpanTulisan.put(ClassUntukTable.KOLOM_ISI, isinya);

    if (identifikasiData == null) {
      // untuk masukan data baru
      identifikasiData = getContentResolver().insert(ContentProviderNya.CONTENT_URI_NYA, simpanTulisan);
    } else {
      // untuk update data
      getContentResolver().update(identifikasiData, simpanTulisan, null, null);
    }
  }

  private void tinggalkanPesanPakaiToast() {
    Toast.makeText(BSbagianIsi.this, "Tulis dulu judul dan isinya",
        Toast.LENGTH_LONG).show();
  }
}


Saya telah mencoba sebisa mungkin untuk menamakan object-object pada class BSbagianIsi.java di atas dengan logika yang sederhana, sehingga kodenya boleh di katakan self-explanatory-lah.

Saya juga telah memutuskan untuk membungkus kedua class di atas dalam satu paket(package) karena kedua class tsb memiliki tipe yang hampir sama, keduanya sama-sama ber-inter-aksi dengan pengguna HP. Nama paketnya nampak pada baris pertama pada kode java com.vik_sintus.bukusakuku. Proses menyimpan class java pada paket sesuai tipe dan kegunaan-nya masing-masing, sering di sebut oleh para ahli coding sebagai 'best practice' dan bahkan akan sangat membantu untuk meringankan beban dalam pengaturan struktur data apabila aplikasinya berkembang menjadi aplikasi besar, dalam arti bobot dan jangkauan pasar.

Oleh karena halaman ini sudah terlalu panjang maka class-class untuk database saya tulis di halaman baru silahkan kesana dengan klik disini

 ........sekarang saya lapar saya pergi makan dulu yah....