fitur:
-
Tampilkan data (RecyclerView + LiveData)
-
Tambah data (form input)
-
Edit data (klik item → buka form edit)
-
Hapus data (long click / tombol delete)
Tambahkan Dependency di build.gradle
dependencies {
val room_version = "2.6.1"
implementation("androidx.room:room-runtime:$room_version")
kapt("androidx.room:room-compiler:$room_version")
implementation("androidx.room:room-ktx:$room_version")
// ViewModel + LiveData
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.4")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.8.4")
}
aktifkan viewBinding :
buildFeatures {
viewBinding = true
}
aktifkan kapt :
plugins {
id("kotlin-kapt")
}
-
Entity→ mendefinisikan tabel -
DAO→ operasi CRUD -
Database→ singleton Room -
Repository→ penghubung DAO & ViewModel -
ViewModel→ pegang data lifecycle-aware -
UI (RecyclerView)→ tampilkan data
1. Struktur Project
app/
└── java/com/example/roomcrud/
├── data/
│ ├── Note.kt
│ ├── NoteDao.kt
│ ├── NoteDatabase.kt
│ └── NoteRepository.kt
├── ui/
│ ├── NoteViewModel.kt
│ ├── NoteAdapter.kt
│ ├── MainActivity.kt
│ └── AddEditNoteActivity.kt
└── ...
2. Entity: Note.kt
package com.example.roomcrud.data
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "notes")
data class Note(
@PrimaryKey(autoGenerate = true) val id: Int = 0,
val title: String,
val content: String
)
3. DAO: NoteDao.kt
package com.example.roomcrud.data
import androidx.lifecycle.LiveData
import androidx.room.*
@Dao
interface NoteDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(note: Note)
@Update
suspend fun update(note: Note)
@Delete
suspend fun delete(note: Note)
@Query("SELECT * FROM notes ORDER BY id DESC")
fun getAllNotes(): LiveData<List<Note>>
}
4. Database: NoteDatabase.kt
package com.example.roomcrud.data
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@Database(entities = [Note::class], version = 1, exportSchema = false)
abstract class NoteDatabase : RoomDatabase() {
abstract fun noteDao(): NoteDao
companion object {
@Volatile private var INSTANCE: NoteDatabase? = null
fun getDatabase(context: Context): NoteDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
NoteDatabase::class.java,
"note_database"
).build()
INSTANCE = instance
instance
}
}
}
}
5. Repository: NoteRepository.kt
package com.example.roomcrud.data
class NoteRepository(private val noteDao: NoteDao) {
val allNotes = noteDao.getAllNotes()
suspend fun insert(note: Note) = noteDao.insert(note)
suspend fun update(note: Note) = noteDao.update(note)
suspend fun delete(note: Note) = noteDao.delete(note)
}
6. ViewModel: NoteViewModel.kt
package com.example.roomcrud.ui
import android.app.Application
import androidx.lifecycle.*
import com.example.roomcrud.data.*
import kotlinx.coroutines.launch
class NoteViewModel(application: Application) : AndroidViewModel(application) {
private val repository: NoteRepository
val allNotes: LiveData<List<Note>>
init {
val noteDao = NoteDatabase.getDatabase(application).noteDao()
repository = NoteRepository(noteDao)
allNotes = repository.allNotes
}
fun insert(note: Note) = viewModelScope.launch { repository.insert(note) }
fun update(note: Note) = viewModelScope.launch { repository.update(note) }
fun delete(note: Note) = viewModelScope.launch { repository.delete(note) }
}
7. Adapter: NoteAdapter.kt
package com.example.roomcrud.ui
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.roomcrud.data.Note
import com.example.roomcrud.databinding.ItemNoteBinding
class NoteAdapter(
private val onEdit: (Note) -> Unit,
private val onDelete: (Note) -> Unit
) : RecyclerView.Adapter<NoteAdapter.NoteViewHolder>() {
private var notes = listOf<Note>()
fun submitList(list: List<Note>) {
notes = list
notifyDataSetChanged()
}
class NoteViewHolder(val binding: ItemNoteBinding) : RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteViewHolder {
val binding = ItemNoteBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return NoteViewHolder(binding)
}
override fun onBindViewHolder(holder: NoteViewHolder, position: Int) {
val note = notes[position]
holder.binding.tvTitle.text = note.title
holder.binding.tvContent.text = note.content
holder.binding.root.setOnClickListener { onEdit(note) }
holder.binding.root.setOnLongClickListener {
onDelete(note)
true
}
}
override fun getItemCount(): Int = notes.size
}
8. Layout Item: item_note.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:padding="12dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tvTitle"
android:textStyle="bold"
android:textSize="18sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tvContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
9. MainActivity: MainActivity.kt
package com.example.roomcrud.ui
import android.content.Intent
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import com.example.roomcrud.data.Note
import com.example.roomcrud.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val viewModel: NoteViewModel by viewModels()
private lateinit var adapter: NoteAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
adapter = NoteAdapter(
onEdit = { note ->
val intent = Intent(this, AddEditNoteActivity::class.java)
intent.putExtra("note_id", note.id)
intent.putExtra("note_title", note.title)
intent.putExtra("note_content", note.content)
startActivity(intent)
},
onDelete = { note ->
viewModel.delete(note)
}
)
binding.recyclerView.adapter = adapter
binding.recyclerView.layoutManager = LinearLayoutManager(this)
viewModel.allNotes.observe(this) { notes ->
adapter.submitList(notes)
}
binding.fabAdd.setOnClickListener {
startActivity(Intent(this, AddEditNoteActivity::class.java))
}
}
}
10. Activity Form: AddEditNoteActivity.kt
package com.example.roomcrud.ui
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import com.example.roomcrud.data.Note
import com.example.roomcrud.databinding.ActivityAddEditNoteBinding
class AddEditNoteActivity : AppCompatActivity() {
private lateinit var binding: ActivityAddEditNoteBinding
private val viewModel: NoteViewModel by viewModels()
private var noteId: Int? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityAddEditNoteBinding.inflate(layoutInflater)
setContentView(binding.root)
noteId = intent.getIntExtra("note_id", -1).takeIf { it != -1 }
val oldTitle = intent.getStringExtra("note_title") ?: ""
val oldContent = intent.getStringExtra("note_content") ?: ""
binding.etTitle.setText(oldTitle)
binding.etContent.setText(oldContent)
binding.btnSave.setOnClickListener {
val note = Note(
id = noteId ?: 0,
title = binding.etTitle.text.toString(),
content = binding.etContent.text.toString()
)
if (noteId == null) {
viewModel.insert(note)
} else {
viewModel.update(note)
}
finish()
}
}
}
11. Layout Main: activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabAdd"
android:src="@android:drawable/ic_input_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_margin="16dp" />
</LinearLayout>
12. Layout Form: activity_add_edit_note.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:padding="16dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/etTitle"
android:hint="Judul"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<EditText
android:id="@+id/etContent"
android:hint="Isi Catatan"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minLines="4" />
<Button
android:id="@+id/btnSave"
android:text="Simpan"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>