Sub CPMK :
- Mahasiswa dapat menjelaskan perintah SQL untuk CRUD database
- Mahasiswa dapat menjelaskan komponen Room Database
- Mahasiswa dapat membuat aplikasi android yang dapat mengambil data Room Database
Alat dan Bahan :
- Laptop atau PC dengan Spesifikasi Prosesor minimal Corei5 dan RAM 8 GB
- Android Studio
- Android Device
Langkah Praktikum :
- Buat Project baru di android studio, dengan kriteria sebagai berikut :
Nama Project | CRUD Room App |
Target & Minimal SDK | Phone and Tablet, API Level 21 |
Tipe Activity | Empty Activity |
Language | Kotlin |
- Melakukan Setup Pada Gradle untuk menginstall library yang dibutuhkan. Buka build.gradle (Module) kemudian tambahkan kode berikut dan klik Sync Now
plugins { id 'com.android.application' id 'kotlin-android' id 'kotlin-android-extensions' id 'kotlin-kapt' } android { compileSdk 31 defaultConfig { applicationId "studio.afandi.crudroomapp" minSdk 21 targetSdk 31 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = '1.8' } buildFeatures { viewBinding true } } dependencies { implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.4.1' implementation 'com.google.android.material:material:1.4.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.2' testImplementation 'junit:junit:' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' // install room implementation "androidx.room:room-runtime:2.4.1" kapt "androidx.room:room-compiler:2.4.1" // install coroutines implementation "androidx.room:room-ktx:2.4.1" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2" }
- Buat package baru dengan cara klik package utama lalu klik kanan, pilih new, kemudian pilih package
Kemudian isikan nama package dengan nama “room”
- Lalu kita buat model atau entity dari table dengan cara menambahkan data class bernama note. Caranya klik kanan pada package room lalu pilih new, kemudian pilih Data Class dan isikan namanya “Note”.
Lalu isikan Data Class Note dengan kode berikut :
package studio.afandi.crudroomapp.room import androidx.room.Entity import androidx.room.PrimaryKey @Entity data class Note ( @PrimaryKey(autoGenerate = true) val id: Int, val title: String, val note: String )
Penjelasan Kode
- Baris 6 kita menambahkan notasi @Entity untuk menjadikan data class Note sebagai entity
- Baris 8 artinya kita menjadikan id sebagai primary key, kemudian nilai id akan di generate secara otomatis tanpa kita harus menuliskan perintah di insert. Jadi langsung otomatis nambah 1, tanpa kita menuliskan nilai id nya.
- Baris 9 sampai 11 menunjukan entity atau kolom yang digunakan pada tabel, ada kolom id, title, dan note
- Tambahkan Interface NoteDao pada package room untuk membuat Data Access Object (DAO)
Kemudian isikan interface NoteDao dengan kode berikut :
import androidx.room.* @Dao interface NoteDao { @Insert suspend fun addNote(note: Note) @Update suspend fun updateNote(note: Note) @Delete suspend fun deleteNote(note: Note) @Query("SELECT * FROM note") suspend fun getNotes(): List<Note> @Query("SELECT * FROM note WHERE id=:note_id") suspend fun getNote(note_id: Int): List<Note> }
Penjelasan Kode
- Baris 3 kita menambahkan notasi @Dao untuk menjadikan interface NoteDao sebagai DAO
- Baris 6 kita tambahkan anotation @Insert untuk menambah data, lalu baris 7 kita buat fungsi addNote()
- Baris 9 kita tambahkan anotation @Update untuk mengubah data, lalu baris 10 kita buat fungsi updateNote()
- Baris 12 kita tambahkan anotation @Delete untuk menambah data, lalu baris 13 kita buat fungsi deleteNote()
- Baris 15 kita tambahan Query Select untuk menampilkan data dari tabel note, lalu baris 16 kita buat fungsi getNotes() yang berisi data dari kelas Note
- Baris 18 kita tambahan Query Select dengan keyword id untuk menampilkan data dengan id tertentu, lalu baris 19 kita buat fungsi getNotes() dengan parameter note_id untuk mengambil id data
- Selesai membuat entity dan DAO, terakhir kita buat instance untuk room database nya. Caranya klik kanan pada package room -> pilih new -> Kotlin Class/File -> Beri nama “NoteDB“. Isi class NoteDB dengan kode kotlin berikut :
@Database( entities = [Note::class], version = 1 ) abstract class NoteDB: RoomDatabase() { abstract fun noteDao(): NoteDao companion object { @Volatile private var instance: NoteDB? = null private val LOCK = Any() operator fun invoke(context: Context) = instance ?: synchronized(LOCK){ instance ?: buildDatabase(context).also{ instance = it } } private fun buildDatabase(context: Context)= Room.databaseBuilder( context.applicationContext, NoteDB::class.java, "note12345.db" ).build() } }
Penjelasan Kode :
- Baris 1 – 4 untuk menjadikan data class Note digunakan sebagai database, versi database adalah 1
- Baris 6 membuat abstract class NoteDB akan mewarisi fungsi dari class RoomDatabase
- Baris 19 – 23 kita membuat fungsi buildDatabase untuk membuat database note12345.db sebagai tempat penyimpanan data
Desain Layout
- Buka activity_main.xml, kita akan mendesain layout untuk halaman utama. Tambahkan kode berikut untuk membuat tampilan halaman main.
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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" tools:context=".MainActivity"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/list_note" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toTopOf="@+id/button_create" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" tools:listitem="@layout/adapter_main"/> <Button android:id="@+id/button_create" android:layout_width="0dp" android:layout_height="wrap_content" android:text="Tulis Catatan" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintBottom_toBottomOf="parent" android:layout_margin="10dp"/> </androidx.constraintlayout.widget.ConstraintLayout>
Pada baris kode diatas kita menambahkan RecyclerView untuk menampilkan data, kemudian satu Button untuk menambah data baru. Apabila terjadi error pada attribute listitem, hal itu wajar karena kita belum menambahkan file adapter_main.xml. Buat file adapter_main.xml dengan cara klik kanan pada layout, kemudian klik new, pilih layout resource file, beri nama adapter_main. Pada file adapter_main.xml tambahkan kode berikut :
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:padding="10dp"> <TextView android:id="@+id/text_title" android:layout_width="0dp" android:layout_height="wrap_content" tools:text="Nanti kita cerita hari ini" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toStartOf="@+id/icon_edit"/> <ImageView android:id="@+id/icon_edit" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_edit" android:padding="10dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintEnd_toStartOf="@+id/icon_delete"/> <ImageView android:id="@+id/icon_delete" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_delete" android:padding="10dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintEnd_toEndOf="parent"/> </androidx.constraintlayout.widget.ConstraintLayout>
Akan terjadi error pada attribute src, hal ini dikarenakan kita belum menambah icon edit dan icon delete. Untuk menambah icon silahkan klik kanan pada Drawable -> Pilih New -> Pilih Vector Asset. Kemudian ketikan ditombol pencarian keyword “edit”. Pilih icon edit bergambar pensil.
Klik OK, kemudian ganti nama menjadi ic_edit
Setelah itu klik tambol Next dan klik Finish, maka icon edit sudah tersimpan di folder Drawable
Lakukan hal yang sama untuk membuat icon delete. Cari di tombol pencarian dengan keyword “delete”
- Buat Activity baru dengan cara klik kanan pada package utama kemudian klik new -> Activity -> Empty Activity. Kemudian beri nama EditActivity. Pada file activity_edit.xml silahkan tambahkan kode berikut ini :
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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="20dp" tools:context=".EditActivity"> <EditText android:id="@+id/edit_title" android:layout_width="0dp" android:layout_height="wrap_content" android:hint="Judul" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" android:background="@drawable/edittextstyle"/> <EditText android:id="@+id/edit_note" android:layout_width="0dp" android:layout_height="wrap_content" android:hint="Tulis Catatan" android:minLines="3" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="@+id/edit_title" android:layout_marginTop="10dp" android:gravity="top" android:background="@drawable/edittextstyle"/> <Button android:id="@+id/button_save" android:layout_width="0dp" android:layout_height="wrap_content" android:text="SAVE" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="@+id/edit_note" android:layout_marginTop="20dp"/> <Button android:id="@+id/button_update" android:layout_width="0dp" android:layout_height="wrap_content" android:text="UPDATE" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="@+id/button_save" android:layout_marginTop="10dp"/> </androidx.constraintlayout.widget.ConstraintLayout>
Apabila terjadi error pada attribute background, lakukan penambahan file edittextstyle. Caranya klik pada pada direktori drawable -> klik New -> Pilih Drawable Resource File. Kemudian isikan File name dengan edittextstyle dan isikan dengan kode berikut.
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <corners android:radius="10dp" /> <padding android:bottom="15dp" android:right="15dp" android:left="15dp" android:top="15dp"/> <solid android:color="#f3f6f4"/> </shape>
Sehingga tampilan dari activity_edit.xml menjadi sebagai berikut :
- Sebelum menambahkan kode di EditActivity, kita perlu menambahkan dulu sebuah class yang menampung id dari tiap data. Buat class baru pada package room dengan cara klik kanan pada package room, pilih new, pilih Kotlin Class/File, beri nama Constant.
Kemudian isikan class Contant dengan kode kotlin berikut :
class Constant { companion object { const val TYPE_READ = 0 const val TYPE_CREATE = 1 const val TYPE_UPDATE = 2 } }
Penjelasan Kode :
- Baris 2 digunakan untuk menyimpan id 0 ke dalam TYPE_READ untuk membaca data
- Baris 3 digunakan untuk menyimpan id 1 ke dalam TYPE_CREATE untuk menambah data
- Baris 4 digunakan untuk menyimpan id 2 ke dalam TYPE_UPDATE untuk mengupdate data
- Setelah class Contant berhasil dibuat, selanjutnya kita isi file EditActivity dengan kode kotlin berikut :
class EditActivity : AppCompatActivity() { val db by lazy { NoteDB(this)} private var noteId: Int = 0 private lateinit var binding : ActivityEditBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityEditBinding.inflate(layoutInflater) setContentView(binding.root) setupView() setupListener() } fun setupView(){ supportActionBar!!.setDisplayHomeAsUpEnabled(true) val intentType = intent.getIntExtra("intent_type", 0) when(intentType){ Constant.TYPE_CREATE -> { binding.buttonUpdate.visibility = View.GONE } Constant.TYPE_READ -> { binding.buttonSave.visibility = View.GONE binding.buttonUpdate.visibility = View.GONE getNote() } Constant.TYPE_UPDATE -> { binding.buttonSave.visibility = View.GONE getNote() } } } private fun setupListener() { binding.buttonSave.setOnClickListener { CoroutineScope(Dispatchers.IO).launch { db.noteDao().addNote( Note(0, binding.editTitle.text.toString(), binding.editNote.text.toString()) ) finish() } } binding.buttonUpdate.setOnClickListener { CoroutineScope(Dispatchers.IO).launch { db.noteDao().updateNote( Note(noteId, binding.editTitle.text.toString(), binding.editNote.text.toString()) ) finish() } } } fun getNote(){ noteId = intent.getIntExtra("intent_id", 0) CoroutineScope(Dispatchers.IO).launch { val notes = db.noteDao().getNote(noteId)[0] binding.editTitle.setText(notes.title) binding.editNote.setText(notes.note) } } override fun onSupportNavigateUp(): Boolean { onBackPressed() return super.onSupportNavigateUp() } }
Penjelasan Kode :
- Baris 3 kita membuat variable db yang berisi instance dari class NoteDB. Fungsi Lazy digunakan untuk mendeklarasikan nilai properti saat pertama kali dijalankan
- Baris 4 memberikan nilai 0 pada variable noteId. Jadi untuk melakukan edit data, identifiernya id 0
- Baris 16 – 33 membuat fungsi setupView(), didalamnya terdapat perintah untuk menambahkan button navigation up. Kemudian juga terdapat tipe intent seperti TYPE_CREATE untuk menulis data baru (button yang ditampilkan button save). Ada pula TYPE_UPDATE untuk mengedit data (button yang ditampilkan button update). Terakhir terdapat TYPE_READ untuk membaca data (button yang ditampilkan adalah button save dan update)
- Baris 35 – 53 terdapat fungsi setupListener() yang berfungsi untuk memberikan aksi ketika button di klik. Jika button save di klik maka fungsi addNote() dipanggil dan terjadi penambahan data. Namun jika button edit yang diklik maka fungsi updateNote() aktif dan akan mengupdate data yang telah diubah.
- Baris 55 – 62 terdapat fungsi getNote() yang berfungsi untuk mengabil data yang kita pilih dan menampilkannya ke EditText.
- Baris 64 – 67 merupakan fungsi untuk kembali ke halaman sebelumnya
- Untuk dapat menampilkan data ke RecyclerView, maka kita perlu menambahkan Adapter. Sekarang mari kita buat Class Adapater dengan cara klik kanan pada package utama, pilih new, pilih Kotlin Class/File, beri nama NoteAdapter. Isi class NoteAdapter dengan kode kotlin berikut :
class NoteAdapter (private var notes: ArrayList<Note>, private val listener: OnAdapterListener) : RecyclerView.Adapter<NoteAdapter.NoteViewHolder>(){ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteViewHolder { return NoteViewHolder( LayoutInflater.from(parent.context) .inflate( R.layout.adapter_main, parent, false ) ) } override fun getItemCount() = notes.size override fun onBindViewHolder(holder: NoteViewHolder, position: Int) { val note = notes[position] holder.view.text_title.text = note.title holder.view.text_title.setOnClickListener { listener.onRead(note) } holder.view.icon_edit.setOnClickListener { listener.onUpdate(note) } holder.view.icon_delete.setOnClickListener { listener.onDelete(note) } } inner class NoteViewHolder(val view: View) : RecyclerView.ViewHolder(view) fun setData(newList: List<Note>) { notes.clear() notes.addAll(newList) notifyDataSetChanged() } interface OnAdapterListener { fun onRead(note: Note) fun onUpdate(note: Note) fun onDelete(note: Note) } }
- Terakhir tambahkan kode ke MainActivity seperti berikut :
class MainActivity : AppCompatActivity() { private lateinit var binding : ActivityMainBinding lateinit var noteAdapter: NoteAdapter val db by lazy { NoteDB(this) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) setupListener() setupRecyclerView() } override fun onStart() { super.onStart() loadNote() } fun loadNote(){ CoroutineScope(Dispatchers.IO).launch { val notes = db.noteDao().getNotes() Log.d("MainActivity", "dbResponse: $notes") withContext(Dispatchers.Main){ noteAdapter.setData(notes) } } } private fun setupListener() { binding.buttonCreate.setOnClickListener { //startActivity(Intent(this, EditActivity::class.java)) intentEdit(0,Constant.TYPE_CREATE) } } fun intentEdit(noteId: Int, intentType: Int){ startActivity( Intent(applicationContext, EditActivity::class.java) .putExtra("intent_id", noteId) .putExtra("intent_type", intentType) ) } private fun setupRecyclerView() { noteAdapter = NoteAdapter(arrayListOf(), object : NoteAdapter.OnAdapterListener{ override fun onRead(note: Note) { intentEdit(note.id, Constant.TYPE_READ) } override fun onUpdate(note: Note) { intentEdit(note.id, Constant.TYPE_UPDATE) } override fun onDelete(note: Note) { deleteDialog(note) } }) binding.listNote.apply { layoutManager = LinearLayoutManager(applicationContext) adapter = noteAdapter } } private fun deleteDialog(note: Note){ val alertDialog = AlertDialog.Builder(this) alertDialog.apply { setTitle("Konfirmasi") setMessage("Yakin Hapus ${note.title}?" ) setNegativeButton("Batal") { dialogInterface, i -> dialogInterface.dismiss() } setPositiveButton("Hapus") { dialogInterface, i -> dialogInterface.dismiss() CoroutineScope(Dispatchers.IO).launch { db.noteDao().deleteNote(note) loadNote() } } } alertDialog.show() } }
- Apabila tidak ada yang error, silahkan running program dan anda akan mendapatkan hasil sebagai berikut:
Mantab penjelasan tutorialnya lengkap dan berhasil di compile. Thanx.
cara copas gradle nya itu gimana ya? udah saya copas terus saya sync kok masih tidak bisa