PPB - Pertemuan 13 - CRUD Registrasi Mahasiswa Room Database
5025231245 | Rafie Zaidan Umara | Pertemuan 13 | PPB C
CRUD Registrasi Mahasiswa Room Database
Student Registration App merupakan aplikasi Android sederhana yang dirancang untuk mensimulasikan proses pendaftaran mahasiswa secara digital. Aplikasi ini memungkinkan pengguna mengisi data diri melalui formulir pendaftaran yang interaktif dan mudah digunakan. Pengembangan aplikasi dilakukan menggunakan bahasa pemrograman Kotlin dan Jetpack Compose dengan menerapkan konsep antarmuka modern yang responsif serta user-friendly.
1. Membuat New Project
Tahap awal pembuatan aplikasi Student Registration App dilakukan dengan membuka Android Studio dan memilih menu “New Project”. Selanjutnya dipilih template “Empty Activity” agar aplikasi memiliki struktur dasar yang sederhana, kemudian pengguna mengisi konfigurasi project dengan nama “Student Registration App” serta memilih bahasa pemrograman Kotlin sebagai bahasa utama untuk pengembangan aplikasi Android menggunakan Jetpack Compose.
2. Struktur Project
student-registration-app/
├── app/ # Main application module
│ ├── src/
│ │ ├── androidTest/ # Instrumented tests
│ │ │ └── java/.../ExampleInstrumentedTest.kt
│ │ │
│ │ ├── main/
│ │ │ ├── java/
│ │ │ │ ├── com/example/studentregistrationapp/ # Activity/Screen layer
│ │ │ │ │ ├── MainActivity.kt
│ │ │ │ │ ├── AddEditStudentActivity.kt
│ │ │ │ │ ├── StudentDetailActivity.kt
│ │ │ │ │ ├── StatisticsActivity.kt
│ │ │ │ │ └── ui/theme/
│ │ │ │ │ ├── Color.kt
│ │ │ │ │ ├── Theme.kt
│ │ │ │ │ └── Type.kt
│ │ │ │ │
│ │ │ │ ├── adapter/ # UI Adapters
│ │ │ │ │ └── StudentAdapter.kt
│ │ │ │ │
│ │ │ │ ├── data/ # Data layer
│ │ │ │ │ ├── entity/
│ │ │ │ │ │ └── Student.kt # Data model
│ │ │ │ │ ├── dao/
│ │ │ │ │ │ └── StudentDao.kt # Database access object
│ │ │ │ │ ├── database/
│ │ │ │ │ │ └── StudentDatabase.kt
│ │ │ │ │ └── repository/
│ │ │ │ │ └── StudentRepository.kt
│ │ │ │ │
│ │ │ │ ├── utils/ # Utility functions
│ │ │ │ │ ├── DateUtils.kt
│ │ │ │ │ └── ValidationUtils.kt
│ │ │ │ │
│ │ │ │ └── viewmodel/ # ViewModel layer
│ │ │ │ └── StudentViewModel.kt
│ │ │ │
│ │ │ ├── res/ # Resources
│ │ │ │ ├── drawable/ # Drawable resources
│ │ │ │ │ ├── bg_chip_semester.xml
│ │ │ │ │ ├── ic_delete.xml
│ │ │ │ │ ├── ic_edit.xml
│ │ │ │ │ └── ic_launcher_*
│ │ │ │ │
│ │ │ │ ├── layout/ # XML layouts
│ │ │ │ │ ├── activity_main.xml
│ │ │ │ │ ├── activity_add_edit_student.xml
│ │ │ │ │ ├── activity_statistics.xml
│ │ │ │ │ ├── activity_student_detail.xml
│ │ │ │ │ ├── item_student.xml
│ │ │ │ │ ├── item_detail_row.xml
│ │ │ │ │ └── item_dropdown.xml
│ │ │ │ │
│ │ │ │ ├── menu/
│ │ │ │ │ └── menu_main.xml
│ │ │ │ │
│ │ │ │ ├── mipmap-*/ # App icons (multiple densities)
│ │ │ │ │ ├── mipmap-hdpi/
│ │ │ │ │ ├── mipmap-mdpi/
│ │ │ │ │ ├── mipmap-xhdpi/
│ │ │ │ │ ├── mipmap-xxhdpi/
│ │ │ │ │ └── mipmap-xxxhdpi/
│ │ │ │ │
│ │ │ │ └── values/ # String resources & colors
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── themes.xml
│ │ │ │
│ │ │ └── AndroidManifest.xml
│ │ │
│ │ └── test/ # Unit tests
│ │ └── java/.../ExampleUnitTest.kt
│ │
│ ├── build.gradle.kts # App-level dependencies & config
│ └── proguard-rules.pro
│
├── gradle/
│ ├── libs.versions.toml # Dependency versions
│ └── wrapper/
│
├── build.gradle.kts # Project-level configuration
├── settings.gradle.kts
├── gradle.properties
├── gradlew / gradlew.bat
└── local.properties
3. Layer Data (Data/)
3.1 app/src/main/java/data/entity/Student.kt
Mendefinisikan model data atau tabel database untuk entitas Mahasiswa. Berisi field seperti id, nim, name, email, dll
package com.example.studentregistration.data.entity
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "students")
data class Student(
@PrimaryKey(autoGenerate = true)
val id: Int = 0,
val nim: String,
val name: String,
val email: String,
val phone: String,
val prodi: String,
val semester: Int,
val address: String,
val gender: String,
val registeredAt: Long = System.currentTimeMillis()
)
3.2 app/src/main/java/data/database/StudentDatabase.kt
Kelas abstrak yang menginisialisasi Room Database. Menggunakan pola Singleton untuk memastikan hanya ada satu instance database yang berjalan.
package com.example.studentregistration.data.database
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import com.example.studentregistration.data.dao.StudentDao
import com.example.studentregistration.data.entity.Student
@Database(
entities = [Student::class],
version = 1,
exportSchema = false
)
abstract class StudentDatabase : RoomDatabase() {
abstract fun studentDao(): StudentDao
companion object {
@Volatile
private var INSTANCE: StudentDatabase? = null
fun getDatabase(context: Context): StudentDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
StudentDatabase::class.java,
"student_database"
)
.fallbackToDestructiveMigration()
.build()
INSTANCE = instance
instance
}
}
}
}
3.3 data/repository/StudentRepository.kt
Perantara antara ViewModel dan Data Access Object (DAO). Berfungsi untuk memisahkan logika akses data dari logika UI
package com.example.studentregistration.data.repository
import androidx.lifecycle.LiveData
import com.example.studentregistration.data.dao.StudentDao
import com.example.studentregistration.data.entity.Student
class StudentRepository(private val studentDao: StudentDao) {
val allStudents: LiveData<List<Student>> = studentDao.getAllStudents()
val totalStudents: LiveData<Int> = studentDao.getTotalStudents()
val allProdi: LiveData<List<String>> = studentDao.getAllProdi()
suspend fun insert(student: Student): Long {
return studentDao.insertStudent(student)
}
suspend fun update(student: Student) {
studentDao.updateStudent(student)
}
suspend fun delete(student: Student) {
studentDao.deleteStudent(student)
}
fun getStudentById(id: Int): LiveData<Student> {
return studentDao.getStudentById(id)
}
fun searchStudents(query: String): LiveData<List<Student>> {
return studentDao.searchStudents(query)
}
fun getStudentsByProdi(prodi: String): LiveData<List<Student>> {
return studentDao.getStudentsByProdi(prodi)
}
fun getStudentCountByProdi(prodi: String): LiveData<Int> {
return studentDao.getStudentCountByProdi(prodi)
}
suspend fun deleteAll() {
studentDao.deleteAllStudents()
}
}
4. Layer ViewModel (ui/viewmodel/)
4.1 ui/viewmodel/StudentViewModel.kt
Pusat logika bisnis. Bertanggung jawab mengambil data dari repository dan menyediakannya untuk UI melalui LiveData. Memproses search query dan operasi CRUD (Create, Read, Update, Delete).
package com.example.studentregistration.ui.viewmodel
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.switchMap
import com.example.studentregistration.data.database.StudentDatabase
import com.example.studentregistration.data.entity.Student
import com.example.studentregistration.data.repository.StudentRepository
import kotlinx.coroutines.launch
class StudentViewModel(application: Application) : AndroidViewModel(application) {
private val repository: StudentRepository
val allStudents: LiveData<List<Student>>
val totalStudents: LiveData<Int>
val allProdi: LiveData<List<String>>
private val _searchQuery = MutableLiveData<String>("")
val searchQuery: LiveData<String> = _searchQuery
// Lazy initialization for displayedStudents
val displayedStudents: LiveData<List<Student>> by lazy {
_searchQuery.switchMap { query ->
if (query.isNullOrBlank()) {
repository.allStudents
} else {
repository.searchStudents(query)
}
}
}
private val _insertResult = MutableLiveData<Long?>()
val insertResult: LiveData<Long?> = _insertResult
private val _operationSuccess = MutableLiveData<Boolean>(false)
val operationSuccess: LiveData<Boolean> = _operationSuccess
init {
val studentDao = StudentDatabase.getDatabase(application).studentDao()
repository = StudentRepository(studentDao)
allStudents = repository.allStudents
totalStudents = repository.totalStudents
allProdi = repository.allProdi
}
fun resetOperationStatus() {
_operationSuccess.value = false
_insertResult.value = null
}
fun insert(student: Student) = viewModelScope.launch {
val result = repository.insert(student)
_insertResult.postValue(result)
_operationSuccess.postValue(true)
}
fun update(student: Student) = viewModelScope.launch {
repository.update(student)
_operationSuccess.postValue(true)
}
fun delete(student: Student) = viewModelScope.launch {
repository.delete(student)
_operationSuccess.postValue(true)
}
fun getStudentById(id: Int): LiveData<Student> {
return repository.getStudentById(id)
}
fun setSearchQuery(query: String) {
_searchQuery.value = query
}
fun getStudentsByProdi(prodi: String): LiveData<List<Student>> {
return repository.getStudentsByProdi(prodi)
}
fun getStudentCountByProdi(prodi: String): LiveData<Int> {
return repository.getStudentCountByProdi(prodi)
}
fun deleteAll() = viewModelScope.launch {
repository.deleteAll()
}
}
class StudentViewModelFactory(private val application: Application) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(StudentViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return StudentViewModel(application) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
5. Layer UI / Screens (ui/screens/)
5.1 ui/screens/MainActivity.kt
Halaman utama (Dashboard) yang menampilkan daftar mahasiswa dalam RecyclerView dan menyediakan fungsi pencarian.
5.2 ui/screens/AddEditStudentActivity.kt
Halaman untuk menambah data mahasiswa baru atau mengedit data yang sudah ada. Berisi form input dan validasi data.
5.3 ui/screens/StudentDetailActivity.kt
Halaman yang menampilkan informasi lengkap satu mahasiswa secara detail.
5.4 ui/screens/StatisticsActivity.kt
Halaman untuk menampilkan statistik jumlah mahasiswa per program studi.
6. Layer UI Adapter (ui/adapter/)
6.1 ui/adapter/StudentAdapter.kt
Mengatur bagaimana data mahasiswa ditampilkan di dalam RecyclerView di MainActivity. Mengelola click listener untuk detail, edit, dan hapus data.
7. Utilities (utils/)
7.1 utils/ValidationUtils.kt
Kumpulan fungsi pembantu untuk memvalidasi input form (misal: validasi format email, panjang NIM, dan nomor HP).
package com.example.studentregistration.utils
object ValidationUtils {
fun isValidNim(nim: String): Boolean {
return nim.isNotBlank() && nim.length in 8..12 && nim.all { it.isDigit() }
}
fun isValidName(name: String): Boolean {
return name.isNotBlank() && name.length >= 3
}
fun isValidEmail(email: String): Boolean {
return email.isNotBlank() && android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches()
}
fun isValidPhone(phone: String): Boolean {
val cleanPhone = phone.replace("[\\s-]".toRegex(), "")
return cleanPhone.isNotBlank() && cleanPhone.length in 10..13 &&
(cleanPhone.startsWith("08") || cleanPhone.startsWith("+62"))
}
fun isValidSemester(semester: String): Boolean {
val sem = semester.toIntOrNull() ?: return false
return sem in 1..14
}
}
7.2 utils/DateUtils.kt
Fungsi pembantu untuk memformat timestamp menjadi format tanggal yang mudah dibaca pengguna.
package com.example.studentregistration.utils
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
object DateUtils {
fun formatDate(timestamp: Long): String {
val sdf = SimpleDateFormat("dd MMMM yyyy, HH:mm", Locale("id", "ID"))
return sdf.format(Date(timestamp))
}
fun formatDateShort(timestamp: Long): String {
val sdf = SimpleDateFormat("dd MMM yyyy", Locale("id", "ID"))
return sdf.format(Date(timestamp))
}
}
8. Resource Files (res/)
8.1 app/src/main/res/layout/item_student.xml
Layout untuk satu item di dalam daftar mahasiswa (digunakan oleh StudentAdapter).
Konfigurasi utama aplikasi yang mendefinisikan activity, permissions, dan icon aplikasi.
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.StudentRegistration">
<activity
android:name="com.example.studentregistration.ui.screens.MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.StudentRegistration">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.example.studentregistration.ui.screens.StudentDetailActivity" />
<activity android:name="com.example.studentregistration.ui.screens.StatisticsActivity" />
<activity android:name="com.example.studentregistration.ui.screens.AddEditStudentActivity" />
</application>
</manifest>
8.3 app/src/main/res/values/themes.xml
Definisi tema warna dan gaya aplikasi (termasuk primary color, error color, dll).
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="Theme.StudentRegistration" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryVariant">@color/primary_dark</item>
<item name="colorOnPrimary">@color/white</item>
<item name="colorSecondary">@color/primary</item>
<item name="colorSecondaryVariant">@color/primary_dark</item>
<item name="colorOnSecondary">@color/white</item>
<item name="android:statusBarColor">@color/primary_dark</item>
<item name="colorSurface">@color/white</item>
<item name="colorError">@color/error</item>
</style>
<style name="Theme.StudentRegistrationApp" parent="Theme.StudentRegistration" />
</resources>
9. Kesimpulan
Project StudentRegistration ini adalah aplikasi manajemen data mahasiswa berbasis Android yang mengimplementasikan arsitektur MVVM (Model-View-ViewModel) secara solid, dengan memanfaatkan teknologi modern seperti Room Database, LiveData, dan Kotlin Coroutines untuk memastikan pengelolaan data yang efisien serta antarmuka yang responsif. Melalui pengembangan aplikasi ini, Anda tidak hanya berhasil mengimplementasikan alur CRUD yang lengkap dan fitur statistik yang fungsional, tetapi juga memperoleh pembelajaran berharga mengenai debugging, sinkronisasi manifes, serta pengelolaan siklus hidup komponen Material Design yang krusial untuk menciptakan pengalaman pengguna yang stabil dan bebas dari crash.
Comments
Post a Comment