Snippet

CRUD Android dengan Room (Kotlin)

19 Sep 2025Khaidir Fahram

 

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>