Tugas 9 - Membuat Aplikasi Dessert Clicker

Aplikasi "Dessert Clicker" ini merupakan contoh yang lebih kompleks dan mendekati aplikasi nyata. Di sini kita akan membahas tidak hanya tentang UI, tetapi juga tentang siklus hidup (lifecycle) Activity, state yang persisten, pemisahan logika, dan cara berinteraksi dengan komponen inti Android seperti Intent dari dalam Jetpack Compose.

Manajemen Lifecycle Activity dan State yang Persisten


// In MainActivity class
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    Log.d(TAG, "onCreate Called")
    //...
}
// ... other lifecycle methods (onStart, onResume, etc.)

// In DessertClickerApp composable
var revenue by rememberSaveable { mutableStateOf(0) }
var dessertsSold by rememberSaveable { mutableStateOf(0) }
        

Aplikasi ini secara eksplisit meng-override dan mencatat (log) berbagai callback dari siklus hidup Activity seperti onCreate, onStart, dan onPause. Ini menunjukkan bagaimana state dari sebuah Activity dikelola. Di sisi Compose, state utama seperti revenue dan dessertsSold menggunakan rememberSaveable. Berbeda dengan remember, rememberSaveable akan menyimpan state bahkan setelah terjadi perubahan konfigurasi (misalnya rotasi layar) atau saat proses aplikasi dihentikan sementara oleh sistem. Ini sangat krusial untuk aplikasi game agar progres pemain tidak hilang.

Struktur Aplikasi Menggunakan `Scaffold`


Scaffold(
    topBar = {
        DessertClickerAppBar(...)
    }
) { contentPadding ->
    DessertClickerScreen(
        //...
        modifier = Modifier.padding(contentPadding)
    )
}
        

Aplikasi ini menggunakan Scaffold, sebuah komponen layout dari Material Design yang menyediakan struktur standar untuk layar. Scaffold memiliki slot-slot seperti topBar untuk menempatkan App Bar di bagian atas. Konten utama layar (DessertClickerScreen) ditempatkan di dalam body lambda dari Scaffold, dan penting untuk menerapkan contentPadding yang disediakan untuk memastikan konten tidak tertutup oleh App Bar.

Pemisahan Logika Bisnis dari UI


fun determineDessertToShow(
    desserts: List,
    dessertsSold: Int
): Dessert {
    var dessertToShow = desserts.first()
    for (dessert in desserts) {
        if (dessertsSold >= dessert.startProductionAmount) {
            dessertToShow = dessert
        } else {
            break
        }
    }
    return dessertToShow
}
        

Fungsi determineDessertToShow adalah contoh yang sangat baik dari pemisahan logika. Fungsi ini adalah fungsi Kotlin murni, bukan sebuah composable. Tugasnya adalah menentukan dessert mana yang harus ditampilkan berdasarkan jumlah yang telah terjual. Memisahkan logika seperti ini dari UI membuat kode lebih bersih, mudah diuji (testable), dan mudah dikelola.

Berinteraksi dengan Framework Android: Intent untuk Berbagi


private fun shareSoldDessertsInformation(intentContext: Context, dessertsSold: Int, revenue: Int) {
    val sendIntent = Intent().apply {
        action = Intent.ACTION_SEND
        putExtra(...)
        type = "text/plain"
    }
    // ...
    try {
        ContextCompat.startActivity(intentContext, shareIntent, null)
    } catch (e: ActivityNotFoundException) {
        Toast.makeText(...).show()
    }
}
        

Ini menunjukkan bagaimana sebuah aplikasi Compose dapat berinteraksi dengan komponen inti Android. Fungsi ini membuat sebuah Intent dengan ACTION_SEND untuk membagikan data (jumlah dessert terjual dan pendapatan) ke aplikasi lain. Penggunaan LocalContext.current di dalam composable diperlukan untuk mendapatkan Context yang dibutuhkan oleh Intent. Selain itu, blok try-catch untuk menangani ActivityNotFoundException adalah praktik terbaik untuk memastikan aplikasi tidak crash jika tidak ada aplikasi yang bisa menangani aksi berbagi tersebut.

Layar Utama dan Penanganan Klik


// In DessertClickerApp
onDessertClicked = {
    // Update the revenue
    revenue += currentDessertPrice
    dessertsSold++

    // Show the next dessert
    val dessertToShow = determineDessertToShow(desserts, dessertsSold)
    currentDessertImageId = dessertToShow.imageId
    currentDessertPrice = dessertToShow.price
}

// In DessertClickerScreen
Image(
    //...
    modifier = Modifier
        //...
        .clickable { onDessertClicked() },
)
        

Layar utama game menggunakan Box untuk menumpuk gambar latar belakang dan konten game. Gambar dessert diberi modifier .clickable. Ketika diklik, lambda onDessertClicked akan dieksekusi. Lambda ini, yang didefinisikan di `DessertClickerApp`, adalah pusat dari alur game: ia memperbarui pendapatan dan jumlah terjual, lalu memanggil logika `determineDessertToShow` untuk menentukan dessert berikutnya, dan akhirnya memperbarui state gambar dan harga dessert saat ini. Ini adalah siklus reaktif yang lengkap.

Komponen UI Modular untuk Informasi Transaksi


@Composable
private fun TransactionInfo(revenue: Int, dessertsSold: Int, modifier: Modifier = Modifier) {
    Column(modifier = modifier) {
        DessertsSoldInfo(dessertsSold = dessertsSold)
        RevenueInfo(revenue = revenue)
    }
}
        

Bagian bawah layar yang menampilkan informasi pendapatan dan penjualan dipecah menjadi beberapa komponen kecil: TransactionInfo, RevenueInfo, dan DessertsSoldInfo. Memecah UI menjadi komponen-komponen kecil yang memiliki satu tanggung jawab (single-responsibility) membuat kode lebih mudah dibaca dan digunakan kembali. Pola penggunaan Row dengan Arrangement.SpaceBetween untuk menampilkan label dan nilai adalah pola yang sangat umum dan efektif.

Dokumentasi & Referensi

  • Github
  • https://developer.android.com/codelabs/basic-android-kotlin-compose-activity-lifecycle
  • https://kuliahppb.blogspot.com/2024/05/activity-dan-intent.html

Komentar

Postingan populer dari blog ini

Tugas 10: Membuat Aplikasi Unscramble

ETS Proyek 5: Aplikasi Galeri Foto Pribadi

Evaluasi Akhir Semester