CRUD Menggunakan Room Database (ViewModel, Lifecycle, LiveData) – Halo, pada tutorial pemrograman android kali ini kita akan mempelajari belajar CRUD Menggunakan Room Database. CRUD ini singkatan dari Create, Read, Update, dan Delete. Tidak hanya mempelajari Room Database saja, kamu akan belajar singkat mengenai ViewModel, LifeCycle, dan LiveData. Pada tutorial CRUD Room Database ini kita akan meng-implementasikan paketan dari Android Architecture Components dengan kasus kita membuat aplikasi catat pengeluaran yang dinamai aplikasinya “Pengeluaran Que”.
Pada aplikasi Pengeluaran Que ini terdapat fitur :
- List pengeluaran ( Read )
- Tambah pengeluaran ( Create )
- Edit pengeluaran ( Update )
- Hapus satu data ( Delete )
- Hapus semua data ( Delete )
Daftar Isi
- 1 Buat Project
- 2 Setup Gradle
- 3 Setup folder
- 4 Buat class entity
- 5 Buat class AppDatabase
- 6 Buat class DatabaseClient
- 7 Buat class PengeluaranDao
- 8 Buat class FunctionHelper
- 9 Persiapan Drawable
- 10 Buat layout Item Pengeluaran
- 11 Buat class PengeluaranAdapter
- 12 Buat class MainViewModel
- 13 Edit layout activity_main.xml
- 14 Class MainActivity
- 15 Buat Activity AddDataActivity
- 16 Layout activity_add_data
- 17 Class AddDataActivity
- 18 Buat class AddDataViewModel
- 19 Jalankan aplikasi
- 20 Download Project
Buat Project
Buat project dengan nama Pengeluaran Que lalu pilih empty activity terlebih dahulu
Setup Gradle
Buka file build.gradle (Module:app) lalu tambahkan depedency berikut :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.recyclerview:recyclerview:1.1.0' implementation 'androidx.vectordrawable:vectordrawable:1.1.0' implementation 'com.google.android.material:material:1.1.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' // Room components implementation "android.arch.persistence.room:runtime:$rootProject.roomVersion" annotationProcessor "android.arch.persistence.room:compiler:$rootProject.roomVersion" androidTestImplementation "android.arch.persistence.room:testing:$rootProject.roomVersion" // Lifecycle components implementation "android.arch.lifecycle:extensions:$rootProject.archLifecycleVersion" annotationProcessor "android.arch.lifecycle:compiler:$rootProject.archLifecycleVersion" // RX JAVA implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' implementation 'io.reactivex.rxjava2:rxjava:2.2.10' |
Untuk full source codenya sebagai berikut :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
apply plugin: 'com.android.application' android { compileSdkVersion 29 buildToolsVersion "29.0.3" defaultConfig { applicationId "com.app.pengeluaranque" minSdkVersion 21 targetSdkVersion 29 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } viewBinding { enabled = true } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.recyclerview:recyclerview:1.1.0' implementation 'androidx.vectordrawable:vectordrawable:1.1.0' implementation 'com.google.android.material:material:1.1.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' // Room components implementation "android.arch.persistence.room:runtime:$rootProject.roomVersion" annotationProcessor "android.arch.persistence.room:compiler:$rootProject.roomVersion" androidTestImplementation "android.arch.persistence.room:testing:$rootProject.roomVersion" // Lifecycle components implementation "android.arch.lifecycle:extensions:$rootProject.archLifecycleVersion" annotationProcessor "android.arch.lifecycle:compiler:$rootProject.archLifecycleVersion" // RX JAVA implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' implementation 'io.reactivex.rxjava2:rxjava:2.2.10' } |
Buka file build.gradle (Project: Pengeluaran Que) lalu tambahkan beberapa kode berikut :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
// Top-level build file where you can add configuration options common to all sub-projects/modules. ext { roomVersion = '1.0.0' archLifecycleVersion = '1.0.0' } buildscript { repositories { google() jcenter() mavenCentral() maven { url 'https://maven.google.com' } } dependencies { classpath 'com.android.tools.build:gradle:3.6.3' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { repositories { google() jcenter() maven { url 'https://jitpack.io' } } } task clean(type: Delete) { delete rootProject.buildDir } |
Setup folder
Agar terlihat lebih rapih dan gampang di maintenance buatlah folder sebagai berikut :
- adapter
- model
- -entity
- utils
- -database
- –daos
- view
- -add
- -main

Catatan
Folder entity pada folder model : Folder yang berisi class class yang berisi Entity dari Database. Maksudnya setiap kita ingin meng-inisialisasi tabel database maka buat file classnya di dalam folder entity tersebut.
Buat class entity
Pada folder model-entity buatlah sebuah class bernama Pengeluaran. Jadi, entity Pengeluaran ini berisi kolom uid (auto increment), kolom keterangan, dan kolom harga. Nah, kita juga menggunakan Parcelable untuk kebutuhan mengirim data. Untuk kodenya sebagai berikut :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
@Entity(tableName = "tbl_pengeluaran") public class Pengeluaran implements Parcelable { @PrimaryKey(autoGenerate = true) public int uid; @ColumnInfo(name = "keterangan") public String keterangan; @ColumnInfo(name = "harga") public int harga; public Pengeluaran() { } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(this.uid); dest.writeString(this.keterangan); dest.writeInt(this.harga); } protected Pengeluaran(Parcel in) { this.uid = in.readInt(); this.keterangan = in.readString(); this.harga = in.readInt(); } public static final Creator<Pengeluaran> CREATOR = new Creator<Pengeluaran>() { @Override public Pengeluaran createFromParcel(Parcel source) { return new Pengeluaran(source); } @Override public Pengeluaran[] newArray(int size) { return new Pengeluaran[size]; } }; } |
Buat class AppDatabase
Pada folder utils-database buatlah sebuah abstract class dengan nama AppDatabase. Class AppDatabase ini untuk memanage list entity yang sudah kita buat dan juga sebagai jembatan antara class Dao yang sudah kita siapkan. Untuk kodenya sebagai berikut :
1 2 3 4 |
@Database(entities = {Pengeluaran.class}, version = 1) public abstract class AppDatabase extends RoomDatabase { public abstract PengeluaranDao pengeluaranDao(); } |
Catatan
- PengeluaranDao akan merah abaikan saja, karena kita belum mempersiapkan class tersebut.
- version = 1 : Jika ada penambahan tabel alangkah lebih baiknya version ini +1 dari value sebelumnya agar database pada aplikasi kita melakukan perubahan.
Buat class DatabaseClient
Pada folder utils – database buatlah sebuah class dengan nama DatabaseClient. Class ini khusus untuk meng-inisialisasi Room Database. Untuk kodenya sebagai berikut :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public class DatabaseClient { private static DatabaseClient mInstance; private AppDatabase mAppDatabase; private DatabaseClient(Context context){ mAppDatabase = Room.databaseBuilder(context, AppDatabase.class, "pengeluaranque_db") .fallbackToDestructiveMigration() .build(); } public static synchronized DatabaseClient getInstance(Context context){ if (mInstance == null){ mInstance = new DatabaseClient(context); } return mInstance; } public static DatabaseClient getInstance() { if (mInstance != null) { return mInstance; } throw new IllegalArgumentException("Should use getInstance(Context) at least once before using this method."); } public AppDatabase getAppDatabase(){ return mAppDatabase; } } |
Catatan
pengeluaranque_db ini adalah nama file database kita. Biasanya class DatabaseClient ini bisa dipanggil pada class App kita ataupun pada saat kita ingin meng-konsumsinya.
Buat class PengeluaranDao
Pada folder utils – database – daos buatlah sebuah interface class dengan nama PengeluaranDao. Class ini berfungsi untuk membuat perintah/fungsi langsung ke database. Misalnya, mengambil semua data ataupun fungsi menambahkan data. Untuk kodenya sebagai berikut :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
@Dao public interface PengeluaranDao { @Query("SELECT * FROM tbl_pengeluaran") LiveData<List<Pengeluaran>> getAll(); @Insert(onConflict = OnConflictStrategy.REPLACE) void insertData(Pengeluaran... pengeluarans); @Query("DELETE FROM tbl_pengeluaran") void deleteAllData(); @Query("DELETE FROM tbl_pengeluaran WHERE uid= :uid") void deleteSingleData(int uid); @Query("SELECT SUM(harga) FROM tbl_pengeluaran") LiveData<Integer> getTotalPrice(); @Query("UPDATE tbl_pengeluaran SET keterangan = :keterangan, harga = :harga WHERE uid = :uid") void updateData(String keterangan, int harga, int uid); } |
Catatan
Disini kita akan menggunakan LiveData agar setiap ada perubahan dia langsung mem-broadcast ke subscribernya dan akan ter-otomatis ke listen oleh activity/fragment kita. Untuk lebih jelasnya apa itu LiveData bisa langsung menuju situs resminya : https://developer.android.com/topic/libraries/architecture/livedata?hl=id
Buat class FunctionHelper
Pada folder utils buatlah sebuah class dengan nama FunctionHelper. Class ini berfungsi untuk menyimpan beberapa fungsi yang sering kita gunakan pada class lain, sebagai contoh disini kita akan membuat fungsi untuk mengubah Integer menjadi format rupiah. Untuk kodenya sebagai berikut :
1 2 3 4 |
public static String rupiahFormat(int price) { DecimalFormat formatter = new DecimalFormat("#,###"); return "Rp " + formatter.format(price).replaceAll(",", "."); } |
Persiapan Drawable
Buat drawable baru dengan nama bg_retangle_primary_dark_8 lalu isi dengan kode berikut :
1 2 3 4 5 |
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="@color/colorPrimaryDark" /> <corners android:radius="8dp" /> </shape> |
Tambahkan beberapa icon berikut :
- ic_add_white
- ic_delete_forever
Caranya klik kanan pada folder drawable > new > vector asset.
Buat layout Item Pengeluaran
Buatlah sebuah layout baru dengan nama item_pengeluaran lalu isi dengan kode berikut :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
<?xml version="1.0" encoding="utf-8"?> <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="2dp" android:layout_marginEnd="16dp" android:layout_marginBottom="8dp" app:cardBackgroundColor="@android:color/white" app:cardElevation="4dp" app:contentPadding="12dp"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <androidx.appcompat.widget.AppCompatTextView android:id="@+id/tvPrice" android:layout_width="match_parent" android:layout_height="wrap_content" android:fontFamily="sans-serif-black" android:textColor="@color/primary_text" android:textSize="18sp" tools:text="Rp.50.000" /> <androidx.appcompat.widget.AppCompatTextView android:id="@+id/tvNote" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" android:fontFamily="sans-serif-light" android:textColor="@color/primary_text" android:textSize="14sp" tools:text="Beli bala - bala, tahu, dan pisang goreng" /> </LinearLayout> <androidx.appcompat.widget.AppCompatImageView android:id="@+id/ivDelete" android:layout_width="wrap_content" android:layout_height="wrap_content" app:srcCompat="@drawable/ic_delete_forever_darker_red" android:layout_alignParentEnd="true"/> </RelativeLayout> </androidx.cardview.widget.CardView> |
Buat class PengeluaranAdapter
Pada folder adapter tambahkan class dengan nama PengeluaranAdapter. Pada class ini kita akan melalukan 2 fungsi yaitu onLongTap dan juga click icon deletenya. Untuk kodenyya sebagai berikut :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
public class PengeluaranAdapter extends RecyclerView.Adapter<PengeluaranAdapter.ViewHolder> { private static final String TAG = PengeluaranAdapter.class.getSimpleName(); private Context context; private List<Pengeluaran> list; private PengeluaranAdapterCallback mAdapterCallback; private ItemPengeluaranBinding binding; public PengeluaranAdapter(Context context, List<Pengeluaran> list, PengeluaranAdapterCallback adapterCallback) { this.context = context; this.list = list; this.mAdapterCallback = adapterCallback; } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { binding = ItemPengeluaranBinding.inflate(LayoutInflater .from(parent.getContext()), parent, false); return new ViewHolder(binding); } @Override public void onBindViewHolder(ViewHolder holder, int position) { Pengeluaran item = list.get(position); holder.bindData(item); } @Override public int getItemCount() { return list.size(); } public void clear() { int size = this.list.size(); this.list.clear(); notifyItemRangeRemoved(0, size); } public void addData(List<Pengeluaran> pengeluarans){ this.list = pengeluarans; notifyDataSetChanged(); } public class ViewHolder extends RecyclerView.ViewHolder { ViewHolder(@NonNull ItemPengeluaranBinding itemView) { super(itemView.getRoot()); itemView.getRoot().setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { Pengeluaran pengeluaran = list.get(getAdapterPosition()); mAdapterCallback.onEdit(pengeluaran); return true; } }); binding.ivDelete.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Pengeluaran pengeluaran = list.get(getAdapterPosition()); mAdapterCallback.onDelete(pengeluaran); } }); } void bindData(Pengeluaran item) { int price = item.harga; String initPrice = FunctionHelper.rupiahFormat(price); binding.tvPrice.setText(initPrice); String note = item.keterangan; binding.tvNote.setText(note); } } public interface PengeluaranAdapterCallback { void onEdit(Pengeluaran pengeluaran); void onDelete(Pengeluaran pengeluaran); } } |
Catatan
Pada saat kita meng-klik suatu item dengan lama dia akan membuka page edit.
Buat class MainViewModel
Pada folder view-main tambahkan class dengan nama MainViewModel. Class ini berfungsi untuk inisialisasi ViewModel dan LiveData kita. Untuk kodenya sebagai berikut :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
public class MainViewModel extends AndroidViewModel { private LiveData<List<Pengeluaran>> mPengeluarans; private PengeluaranDao pengeluaranDao; private LiveData<Integer> mTotalPrice; public MainViewModel(@NonNull Application application) { super(application); pengeluaranDao = DatabaseClient.getInstance(application) .getAppDatabase().pengeluaranDao(); mPengeluarans = pengeluaranDao.getAll(); mTotalPrice = pengeluaranDao.getTotalPrice(); } public LiveData<List<Pengeluaran>> getPengeluarans() { return mPengeluarans; } public LiveData<Integer> getTotalPrice() { return mTotalPrice; } public void deleteAllData() { Completable.fromAction(new Action() { @Override public void run() throws Exception { pengeluaranDao.deleteAllData(); } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(); } public void deleteSingleData(final int uid) { Completable.fromAction(new Action() { @Override public void run() throws Exception { pengeluaranDao.deleteSingleData(uid); } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(); } } |
Edit layout activity_main.xml
Pada file layout activity_main.xml tambahakn beberapa komponen view berikut :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/white" tools:context=".view.main.MainActivity"> <androidx.cardview.widget.CardView android:id="@+id/cvTotal" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:layout_marginEnd="16dp" android:layout_marginBottom="2dp" app:cardBackgroundColor="@android:color/white" app:contentPadding="16dp"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <androidx.appcompat.widget.AppCompatTextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:fontFamily="sans-serif" android:text="@string/total_pengeluaran_bulan_ini" android:textColor="@color/primary_text" android:textSize="14sp" /> <androidx.appcompat.widget.AppCompatTextView android:id="@+id/tvTotal" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentEnd="true" android:layout_centerVertical="true" android:fontFamily="sans-serif-black" android:textColor="@color/primary_text" android:textSize="18sp" tools:ignore="RelativeOverlap" tools:text="Rp. 50.000" /> </RelativeLayout> </androidx.cardview.widget.CardView> <androidx.recyclerview.widget.RecyclerView android:id="@+id/rvPengeluarans" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@id/btnHapus" android:layout_below="@id/cvTotal" android:layout_marginTop="16dp" tools:itemCount="2" tools:listitem="@layout/item_pengeluaran" /> <androidx.appcompat.widget.AppCompatButton android:id="@+id/btnHapus" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_marginStart="16dp" android:layout_marginEnd="16dp" android:layout_marginBottom="20dp" android:layout_toStartOf="@id/fabAdd" android:background="@drawable/bg_retangle_primary_dark_8" android:text="@string/hapus_semua" android:textAllCaps="false" android:textColor="@android:color/white" /> <com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/fabAdd" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentEnd="true" android:layout_alignParentBottom="true" android:layout_marginEnd="16dp" android:layout_marginBottom="16dp" android:backgroundTint="@color/colorPrimaryDark" android:src="@drawable/ic_add_white_24" app:fabSize="normal" /> </RelativeLayout> |
Class MainActivity
Pada class MainActivity jangan lupa untuk meng-inisialisasi View Binding. Jika kamu belum mengetahui apa itu View Binding bisa menuju artikel berikut Cara Menggunakan View Binding Android Jetpack. Untuk kode fullnya seperti berikut :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
public class MainActivity extends AppCompatActivity implements PengeluaranAdapter.PengeluaranAdapterCallback { private ActivityMainBinding binding; private PengeluaranAdapter pengeluaranAdapter; private MainViewModel mainViewModel; private List<Pengeluaran> mPengeluarans = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityMainBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); initAdapter(); observeData(); initAction(); } private void initAction() { binding.fabAdd.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { AddDataActivity.startActivity(MainActivity.this, false, null); } }); binding.btnHapus.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mainViewModel.deleteAllData(); binding.tvTotal.setText("0"); } }); } private void initAdapter() { pengeluaranAdapter = new PengeluaranAdapter(this, mPengeluarans, this); binding.rvPengeluarans.setLayoutManager(new LinearLayoutManager(this)); binding.rvPengeluarans.setItemAnimator(new DefaultItemAnimator()); binding.rvPengeluarans.setAdapter(pengeluaranAdapter); } private void observeData() { mainViewModel = ViewModelProviders.of(this).get(MainViewModel.class); mainViewModel.getPengeluarans().observe(this, new Observer<List<Pengeluaran>>() { @Override public void onChanged(List<Pengeluaran> pengeluarans) { if (pengeluarans.isEmpty()) { binding.btnHapus.setVisibility(View.GONE); } else { binding.btnHapus.setVisibility(View.VISIBLE); } pengeluaranAdapter.addData(pengeluarans); } }); mainViewModel.getTotalPrice().observe(this, new Observer<Integer>() { @Override public void onChanged(Integer integer) { if (integer == null) { int totalPrice = 0; String initPrice = FunctionHelper.rupiahFormat(totalPrice); binding.tvTotal.setText(initPrice); } else { int totalPrice = integer; String initPrice = FunctionHelper.rupiahFormat(totalPrice); binding.tvTotal.setText(initPrice); } } }); } @Override public void onEdit(Pengeluaran pengeluaran) { AddDataActivity.startActivity(this, true, pengeluaran); } @Override public void onDelete(Pengeluaran pengeluaran) { int uid = pengeluaran.uid; mainViewModel.deleteSingleData(uid); } } |
Catatan
- mainViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
Ini inisialisasi class mana yang menggunakan extend AndroidViewModel
- mainViewModel.getPengeluarans().observe…
Ini untuk meng-listen hasil dari perubahan yang terjadi
Buat Activity AddDataActivity
Pada folder view-add tambakan empty activity dengan nama AddDataActivity. Activity ini akan berfungsi sebagai membuat dan juga meng-update data.
Layout activity_add_data
Pada file activity_add_data tambahkan komponen view sebagai berikut :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="16dp" tools:context=".view.add.AddDataActivity"> <com.google.android.material.textfield.TextInputLayout android:id="@+id/tilKeterangan" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/keterangan"> <com.google.android.material.textfield.TextInputEditText android:id="@+id/etKeterangan" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="textMultiLine|textCapSentences" android:textSize="14sp" /> </com.google.android.material.textfield.TextInputLayout> <com.google.android.material.textfield.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/tilKeterangan" android:layout_marginTop="16dp" android:hint="@string/harga"> <com.google.android.material.textfield.TextInputEditText android:id="@+id/etHarga" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="number" android:textSize="14sp" /> </com.google.android.material.textfield.TextInputLayout> <androidx.appcompat.widget.AppCompatButton android:id="@+id/btnSimpan" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_marginTop="16dp" android:background="@drawable/bg_retangle_primary_dark_8" android:text="@string/simpan" android:textAllCaps="false" android:textColor="@android:color/white" /> </RelativeLayout> |
Class AddDataActivity
Pada file AddDataActivity jangan lupa untuk meng-inisialisasi View Binding kita. Untuk kode fullnya sebagai berikut :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
public class AddDataActivity extends AppCompatActivity { private static String KEY_IS_EDIT = "key_is_edit"; private static String KEY_DATA = "key_data"; // Untuk kebutuhan data yang akan dipakai pada Activitu AddData public static void startActivity(Context context, boolean isEdit, Pengeluaran pengeluaran) { Intent intent = new Intent(new Intent(context, AddDataActivity.class)); intent.putExtra(KEY_IS_EDIT, isEdit); intent.putExtra(KEY_DATA, pengeluaran); context.startActivity(intent); } private ActivityAddDataBinding binding; private AddDataViewModel addDataViewModel; private boolean mIsEdit = false; private int mUid = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityAddDataBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); addDataViewModel = ViewModelProviders.of(this).get(AddDataViewModel.class); loadData(); initAction(); } private void initAction() { binding.btnSimpan.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String note = binding.etKeterangan.getText().toString(); String price = binding.etHarga.getText().toString(); if (note.isEmpty() || price.isEmpty()) { Toast.makeText(AddDataActivity.this, getString(R.string.error_message_form_empty), Toast.LENGTH_SHORT).show(); } else { if (mIsEdit) { addDataViewModel.updatePengeluaran(mUid, note, Integer.parseInt(price)); } else { addDataViewModel.addPengeluaran(note, Integer.parseInt(price)); } finish(); } } }); } private void loadData() { mIsEdit = getIntent().getBooleanExtra(KEY_IS_EDIT, false); if (mIsEdit) { Pengeluaran pengeluaran = getIntent().getParcelableExtra(KEY_DATA); if (pengeluaran != null) { mUid = pengeluaran.uid; String note = pengeluaran.keterangan; int price = pengeluaran.harga; binding.etKeterangan.setText(note); binding.etHarga.setText(String.valueOf(price)); } } } } |
Catatan
- Fungsi loadData() akan digunakan ketika boolean mIsEdit kita true, yang artinya state dalam keadaan ingin update/edit data.
- Jika AddDataViewModel kita merah abaikan terlbih dahulu karena kita belum mempersiapkan class tersebut.
Buat class AddDataViewModel
Pada folder view – add tambahkan class baru dengan nama AddDataViewModel. Disini kita akan memakai bantuan library Rxjava untuk meng-eksekusi query database kita, karena pada saat eksekusi query perlu dijalankan pada mainthread. Untuk kodenya sebagai berikut :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
public class AddDataViewModel extends AndroidViewModel { private PengeluaranDao pengeluaranDao; public AddDataViewModel(@NonNull Application application) { super(application); pengeluaranDao = DatabaseClient.getInstance(application).getAppDatabase().pengeluaranDao(); } public void addPengeluaran(final String note, final int price) { Completable.fromAction(new Action() { @Override public void run() throws Exception { Pengeluaran pengeluaran = new Pengeluaran(); pengeluaran.keterangan = note; pengeluaran.harga = price; pengeluaranDao.insertData(pengeluaran); } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(); } public void updatePengeluaran(final int uid, final String note, final int price){ Completable.fromAction(new Action() { @Override public void run() throws Exception { pengeluaranDao.updateData(note, price, uid); } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(); } } |
Jalankan aplikasi
Dari tutorial pemrograman android kali ini mengenai CRUD Menggunakan Room Database (ViewModel, Lifecycle, LiveData) kamu secara tidak langsung mempelajari hal berikut :
- CRUD menggunakan Room Database
- Implementasi LiveData
- Implementasi ViewModel
- Implementasi View Binding
- Implementasi Lifecycle
- Implementasi RxJava
Download Project
Kamu bisa juga men-download project dari link Github berikut : https://github.com/farizdotid/Pengeluaran-Que
Sekian tutorial pemrograman android kali ini mengenai CRUD Menggunakan Room Database (ViewModel, Lifecycle, LiveData). Semoga bermanfaat dan seperti biasa jika ingin ada yang ditanyakan bisa langsung berkomenta ya
Kenapa ItemPengeluaranBinding nya cannot resolve method?
coba di rebuild projectnya mas.
gan gimana cara tambah 1 form datepicker di aplikasi ini? tolong sourcenya gan untuk tugas kuliah
Itu contoh lengkanya udah ada mba, tinggal di modifikasi kembali.