1. Sebelum memulai
Pengantar
Pada tahap kursus ini, Anda telah berpengalaman dalam mem-build aplikasi dengan Compose dan memiliki pengetahuan cara membangun aplikasi dengan XML, View, View Binding, dan Fragment. Setelah mem-build aplikasi dengan View, Anda mungkin jadi menghargai kemudahan mem-build aplikasi dengan UI deklaratif seperti Compose. Namun, mungkin ada beberapa kasus yang wajar jika menggunakan View, bukan Compose. Dalam codelab ini, Anda akan mempelajari cara menggunakan Interop View untuk menambahkan komponen View ke dalam aplikasi Compose modern.
Pada saat menulis codelab ini, komponen UI yang Anda tetapkan untuk dibuat belum tersedia di Compose. Ini adalah peluang bagus untuk menggunakan Interop View.
Prasyarat:
- Menyelesaikan kursus Dasar-Dasar Android dengan Compose melalui codelab Mem-build Aplikasi Android dengan View.
 
Yang akan Anda butuhkan
- Komputer yang memiliki akses internet dan Android Studio
 - Perangkat atau emulator
 - Kode awal untuk aplikasi Juice Tracker
 
Yang akan Anda bangun
Dalam codelab ini, Anda perlu mengintegrasikan tiga View ke dalam UI Compose untuk menyelesaikan UI aplikasi Juice Tracker. Ketiganya adalah Spinner, RatingBar, dan AdView. Untuk membangun komponen ini, Anda akan menggunakan Interoperabilitas View, atau disingkat Interop View. Dengan Interop View, Anda sebenarnya dapat menambahkan View ke aplikasi dengan menggabungkannya ke dalam Composable.
 
 
Panduan kode
Dalam codelab ini, Anda akan menggunakan aplikasi JuiceTracker yang sama dari codelab Mem-build Aplikasi Android dengan View dan Menambahkan Compose ke aplikasi berbasis View. Perbedaan dengan versi ini adalah kode awal yang diberikan sepenuhnya dalam Compose. Aplikasi saat ini tidak memiliki input warna dan rating di sheet dialog entri dan banner iklan di bagian atas layar daftar.
Direktori bottomsheet berisi semua komponen UI yang terkait dengan dialog entri. Paket ini harus berisi komponen UI untuk input warna dan rating ketika dibuat.
homescreen berisi komponen UI yang dihosting oleh layar utama, termasuk daftar JuiceTracker. Paket ini nantinya harus berisi banner iklan ketika dibuat.
Komponen UI utama, seperti sheet bawah dan daftar jus akan dihosting di file JuiceTrackerApp.kt.
2. Mendapatkan kode awal
Untuk memulai, download kode awal:
Atau, Anda dapat membuat clone repositori GitHub untuk kode tersebut:
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-juice-tracker.git $ cd basic-android-kotlin-compose-training-juice-tracker $ git checkout compose-starter
- Di Android Studio, buka folder 
basic-android-kotlin-compose-training-juice-tracker. - Buka kode aplikasi Juice Tracker di Android Studio.
 
3. Konfigurasi Gradle
Tambahkan dependensi iklan layanan Play ke file build.gradle.kts aplikasi.
app/build.gradle.kts
android {
   ...
   dependencies {
      ...
      implementation("com.google.android.gms:play-services-ads:22.2.0")
   }
}
4. Penyiapan
Tambahkan nilai berikut ke manifes Android, di atas tag activity, guna mengaktifkan banner iklan untuk pengujian:
AndroidManifest.xml
...
<meta-data
   android:name="com.google.android.gms.ads.APPLICATION_ID"
   android:value="ca-app-pub-3940256099942544~3347511713" />
...
5. Menyelesaikan dialog entri
Di bagian ini, Anda akan menyelesaikan dialog entri dengan membuat indikator lingkaran berputar warna dan batang rating. Indikator lingkaran berputar warna adalah komponen untuk memilih warna, sedangkan batang rating dapat digunakan untuk memilih rating jus. Lihat desain di bawah ini:


Membuat indikator lingkaran berputar warna
Untuk menerapkan indikator lingkaran berputar di Compose, class Spinner harus digunakan. Spinner adalah komponen View, bukan Composable, sehingga harus diimplementasikan menggunakan interop.
- Di direktori 
bottomsheet, buat file baru yang bernamaColorSpinnerRow.kt. - Buat class baru di dalam file yang bernama 
SpinnerAdapter. - Dalam konstruktor untuk 
SpinnerAdapter, tentukan parameter callback yang disebutonColorChangeyang menggunakan parameterInt.SpinnerAdaptermenangani fungsi callback untukSpinner. 
bottomsheet/ColorSpinnerRow.kt
class SpinnerAdapter(val onColorChange: (Int) -> Unit){
}
- Implementasikan antarmuka 
AdapterView.OnItemSelectedListener. 
Dengan menerapkan antarmuka ini, Anda dapat menentukan perilaku klik untuk indikator lingkaran berputar. Anda dapat menyiapkan adaptor ini nanti di Composable.
bottomsheet/ColorSpinnerRow.kt
class SpinnerAdapter(val onColorChange: (Int) -> Unit): AdapterView.OnItemSelectedListener {
}
- Implementasikan fungsi anggota 
AdapterView.OnItemSelectedListener:onItemSelected()danonNothingSelected(). 
bottomsheet/ColorSpinnerRow.kt
class SpinnerAdapter(val onColorChange: (Int) -> Unit): AdapterView.OnItemSelectedListener {
   override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
        TODO("Not yet implemented")
    }
    override fun onNothingSelected(parent: AdapterView<*>?) {
        TODO("Not yet implemented")
    }
}
- Ubah fungsi 
onItemSelected()untuk memanggil fungsi callbackonColorChange()sehingga saat Anda memilih warna, aplikasi akan memperbarui nilai yang dipilih di UI. 
bottomsheet/ColorSpinnerRow.kt
class SpinnerAdapter(val onColorChange: (Int) -> Unit): AdapterView.OnItemSelectedListener {
   override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
        onColorChange(position)
    }
    override fun onNothingSelected(parent: AdapterView<*>?) {
        TODO("Not yet implemented")
    }
}
- Ubah fungsi 
onNothingSelected()untuk menetapkan warna ke0sehingga saat Anda tidak memilih apa pun, warna default adalah warna pertama, yaitu merah. 
bottomsheet/ColorSpinnerRow.kt
class SpinnerAdapter(val onColorChange: (Int) -> Unit): AdapterView.OnItemSelectedListener {
   override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
        onColorChange(position)
    }
    override fun onNothingSelected(parent: AdapterView<*>?) {
        onColorChange(0)
    }
}
SpinnerAdapter, yang menentukan perilaku indikator lingkaran berputar melalui fungsi callback, telah dibuat. Sekarang Anda harus membuat konten indikator lingkaran berputar dan mengisinya dengan data.
- Di dalam file 
ColorSpinnerRow.kt, tetapi di luar classSpinnerAdapter, buat Composable baru yang bernamaColorSpinnerRow. - Dalam tanda tangan metode 
ColorSpinnerRow(), tambahkan parameterIntuntuk posisi indikator lingkaran berputar, fungsi callback yang menggunakan parameterIntdan pengubah. 
bottomsheet/ColorSpinnerRow.kt
...
@Composable
fun ColorSpinnerRow(
    colorSpinnerPosition: Int,
    onColorChange: (Int) -> Unit,
    modifier: Modifier = Modifier
) {
}
- Di dalam fungsi, buat array resource string warna jus menggunakan enum 
JuiceColor. Array ini berfungsi sebagai konten yang akan mengisi indikator lingkaran berputar. 
bottomsheet/ColorSpinnerRow.kt
...
@Composable
fun ColorSpinnerRow(
    colorSpinnerPosition: Int,
    onColorChange: (Int) -> Unit,
    modifier: Modifier = Modifier
) {
   val juiceColorArray =
        JuiceColor.values().map { juiceColor -> stringResource(juiceColor.label) }
}
- Tambahkan Composable 
InputRow()dan teruskan resource string warna untuk label input dan pengubah, yang menentukan baris input tempatSpinnermuncul. 
bottomsheet/ColorSpinnerRow.kt
...
@Composable
fun ColorSpinnerRow(
    colorSpinnerPosition: Int,
    onColorChange: (Int) -> Unit,
    modifier: Modifier = Modifier
) {
   val juiceColorArray =
        JuiceColor.values().map { juiceColor -> stringResource(juiceColor.label) }
   InputRow(inputLabel = stringResource(R.string.color), modifier = modifier) {
   }
}
Berikutnya, Anda akan membuat Spinner. Karena Spinner adalah class View, API interoperabilitas View dari Compose harus digunakan untuk menggabungkannya ke dalam Composable. Hal ini dapat dilakukan dengan Composable AndroidView.
- Untuk menggunakan 
Spinnerdi Compose, buat ComposableAndroidView()dalam isi lambdaInputRow. ComposableAndroidView()membuat elemen atau hierarki View dalam Composable. 
bottomsheet/ColorSpinnerRow.kt
...
@Composable
fun ColorSpinnerRow(
    colorSpinnerPosition: Int,
    onColorChange: (Int) -> Unit,
    modifier: Modifier = Modifier
) {
   val juiceColorArray =
        JuiceColor.values().map { juiceColor -> stringResource(juiceColor.label) }
   InputRow(inputLabel = stringResource(R.string.color), modifier = modifier) {
      AndroidView()
   }
}
Composable AndroidView menggunakan tiga parameter:
factorylambda, yang merupakan fungsi untuk membuat View.- Callback 
update, yang dipanggil saat View yang dibuat difactorydi-inflate. modifiercomposable.

- Untuk menerapkan 
AndroidView, mulailah dengan meneruskan pengubah dan mengisi lebar maksimum layar. - Teruskan lambda untuk parameter 
factory. - Lambda 
factorymenggunakanContextsebagai parameter. Buat classSpinnerlalu teruskan konteksnya. 
bottomsheet/ColorSpinnerRow.kt
...
@Composable
fun ColorSpinnerRow(
    colorSpinnerPosition: Int,
    onColorChange: (Int) -> Unit,
    modifier: Modifier = Modifier
) {
   ...
   InputRow(...) {
      AndroidView(
         modifier = Modifier.fillMaxWidth(),
         factory = { context ->
            Spinner(context)
         }
      )
   }
}
Sama seperti RecyclerView.Adapter yang menyediakan data ke RecyclerView, ArrayAdapter juga menyediakan data ke Spinner. Spinner memerlukan adaptor untuk menyimpan array warna.
- Setel adaptor menggunakan 
ArrayAdapter.ArrayAdaptermemerlukan konteks, tata letak XML, dan array. Teruskansimple_spinner_dropdown_itemuntuk tata letak; tata letak ini disediakan sebagai default dengan Android. 
bottomsheet/ColorSpinnerRow.kt
...
@Composable
fun ColorSpinnerRow(
    colorSpinnerPosition: Int,
    onColorChange: (Int) -> Unit,
    modifier: Modifier = Modifier
) {
   ...
   InputRow(...) {
      AndroidView(
         modifier = Modifier.fillMaxWidth(),
         factory = { context ->
             Spinner(context).apply {
                 adapter =
                     ArrayAdapter(
                         context,
                         android.R.layout.simple_spinner_dropdown_item,
                         juiceColorArray
                     )
             }
         }
      )
   }
}
Callback factory menampilkan instance View yang dibuat di dalamnya. update adalah callback yang menggunakan parameter dari jenis sama yang ditampilkan oleh callback factory. Parameter ini adalah instance View yang di-inflate oleh factory. Dalam hal ini, karena Spinner dibuat di factory, instance Spinner tersebut dapat diakses dalam isi lambda update.
- Tambahkan callback 
updateyang meneruskanspinner. Gunakan callback yang disediakan diupdateuntuk memanggil metodesetSelection(). 
bottomsheet/ColorSpinnerRow.kt
...
@Composable
fun ColorSpinnerRow(
    colorSpinnerPosition: Int,
    onColorChange: (Int) -> Unit,
    modifier: Modifier = Modifier
) {
   ...
   InputRow(...) {
      //...
         },
         update = { spinner ->
             spinner.setSelection(colorSpinnerPosition)
             spinner.onItemSelectedListener = SpinnerAdapter(onColorChange)
         }
      )
   }
}
- Gunakan 
SpinnerAdapteryang Anda buat sebelumnya untuk menetapkan callbackonItemSelectedListener()diupdate. 
bottomsheet/ColorSpinnerRow.kt
...
@Composable
fun ColorSpinnerRow(
    colorSpinnerPosition: Int,
    onColorChange: (Int) -> Unit,
    modifier: Modifier = Modifier
) {
   ...
   InputRow(...) {
      AndroidView(
         // ...
         },
         update = { spinner ->
             spinner.setSelection(colorSpinnerPosition)
             spinner.onItemSelectedListener = SpinnerAdapter(onColorChange)
         }
      )
   }
}
Kode untuk komponen indikator lingkaran berputar warna sekarang sudah selesai.
- Tambahkan fungsi utilitas berikut untuk mendapatkan indeks enum 
JuiceColor. Anda akan menggunakannya di langkah berikutnya. 
private fun findColorIndex(color: String): Int {
   val juiceColor = JuiceColor.valueOf(color)
   return JuiceColor.values().indexOf(juiceColor)
}
- Implementasikan 
ColorSpinnerRowdi ComposableSheetFormpada fileEntryBottomSheet.kt. Letakkan indikator lingkaran berputar warna setelah teks "Deskripsi", dan di atas tombol. 
bottomsheet/EntryBottomSheet.kt
...
@Composable
fun SheetForm(
   juice: Juice,
   onUpdateJuice: (Juice) -> Unit,
   onCancel: () -> Unit,
   onSubmit: () -> Unit,
   modifier: Modifier = Modifier,
) {
   ...
   TextInputRow(
            inputLabel = stringResource(R.string.juice_description),
            fieldValue = juice.description,
            onValueChange = { description -> onUpdateJuice(juice.copy(description = description)) },
            modifier = Modifier.fillMaxWidth()
        )
        ColorSpinnerRow(
            colorSpinnerPosition = findColorIndex(juice.color),
            onColorChange = { color ->
                onUpdateJuice(juice.copy(color = JuiceColor.values()[color].name))
            }
        )
   ButtonRow(
            modifier = Modifier
                .align(Alignment.End)
                .padding(bottom = dimensionResource(R.dimen.padding_medium)),
            onCancel = onCancel,
            onSubmit = onSubmit,
            submitButtonEnabled = juice.name.isNotEmpty()
        )
    }
}
Membuat input rating
- Buat file baru di direktori 
bottomsheetyang bernamaRatingInputRow.kt. - Dalam file 
RatingInputRow.kt, buat Composable baru yang bernamaRatingInputRow(). - Dalam tanda tangan metode, teruskan 
Intuntuk rating, callback dengan parameterIntuntuk menangani perubahan pemilihan, dan pengubah. 
bottomsheet/RatingInputRow.kt
@Composable
fun RatingInputRow(rating:Int, onRatingChange: (Int) -> Unit, modifier: Modifier = Modifier){
}
- Seperti 
ColorSpinnerRow, tambahkanInputRowke Composable yang berisiAndroidView, seperti yang ditunjukkan dalam kode contoh berikut. 
bottomsheet/RatingInputRow.kt
@Composable
fun RatingInputRow(rating:Int, onRatingChange: (Int) -> Unit, modifier: Modifier = Modifier){
    InputRow(inputLabel = stringResource(R.string.rating), modifier = modifier) {
        AndroidView(
            factory = {},
            update = {}
        )
    }
}
- Dalam isi lambda 
factory, buat instance classRatingBaryang menyediakan jenis batang rating yang diperlukan untuk desain ini. SetelstepSizeke1funtuk menerapkan rating agar hanya berupa bilangan bulat. 
bottomsheet/RatingInputRow.kt
@Composable
fun RatingInputRow(rating:Int, onRatingChange: (Int) -> Unit, modifier: Modifier = Modifier){
    InputRow(inputLabel = stringResource(R.string.rating), modifier = modifier) {
        AndroidView(
            factory = { context ->
                RatingBar(context).apply {
                    stepSize = 1f
                }
            },
            update = {}
        )
    }
}
Jika View di-inflate, rating akan ditetapkan. Ingat kembali bahwa factory akan menampilkan instance RatingBar ke callback update.
- Gunakan rating yang diteruskan ke Composable untuk menetapkan rating bagi instance 
RatingBardi isi lambdaupdate. - Saat rating baru ditetapkan, gunakan callback 
RatingBaruntuk memanggil fungsi callbackonRatingChange()guna memperbarui rating di UI. 
bottomsheet/RatingInputRow.kt
@Composable
fun RatingInputRow(rating:Int, onRatingChange: (Int) -> Unit, modifier: Modifier = Modifier){
    InputRow(inputLabel = stringResource(R.string.rating), modifier = modifier) {
        AndroidView(
            factory = { context ->
                RatingBar(context).apply {
                    stepSize = 1f
                }
            },
            update = { ratingBar ->
                ratingBar.rating = rating.toFloat()
                ratingBar.setOnRatingBarChangeListener { _, _, _ ->
                    onRatingChange(ratingBar.rating.toInt())
                }
            }
        )
    }
}
Sekarang, Composable input rating telah selesai.
- Gunakan composable 
RatingInputRow()diEntryBottomSheet. Tempatkan setelah indikator lingkaran berputar warna dan di atas tombol. 
bottomsheet/EntryBottomSheet.kt
@Composable
fun SheetForm(
    juice: Juice,
    onUpdateJuice: (Juice) -> Unit,
    onCancel: () -> Unit,
    onSubmit: () -> Unit,
    modifier: Modifier = Modifier,
) {
    Column(
        modifier = modifier,
        verticalArrangement = Arrangement.spacedBy(4.dp)
    ) {
        ...
        ColorSpinnerRow(
            colorSpinnerPosition = findColorIndex(juice.color),
            onColorChange = { color ->
                onUpdateJuice(juice.copy(color = JuiceColor.values()[color].name))
            }
        )
        RatingInputRow(
            rating = juice.rating,
            onRatingChange = { rating -> onUpdateJuice(juice.copy(rating = rating)) }
        )
        ButtonRow(
            modifier = Modifier.align(Alignment.CenterHorizontally),
            onCancel = onCancel,
            onSubmit = onSubmit,
            submitButtonEnabled = juice.name.isNotEmpty()
        )
    }
}
Membuat banner iklan
- Dalam paket 
homescreen, buat file baru yang bernamaAdBanner.kt. - Dalam file 
AdBanner.kt, buat Composable baru yang bernamaAdBanner(). 
Tidak seperti Composable sebelumnya yang telah dibuat, AdBanner tidak memerlukan input. Oleh karena itu, Anda tidak perlu menggabungkannya dalam Composable InputRow. Namun, metode ini memerlukan AndroidView.
- Coba Anda membuat banner sendiri menggunakan class 
AdView. Pastikan Anda menetapkan ukuran iklan keAdSize.BANNERdan ID unit iklan ke"ca-app-pub-3940256099942544/6300978111". - Saat 
AdViewdi-inflate, muat iklan menggunakanAdRequest Builder. 
homescreen/AdBanner.kt
@Composable
fun AdBanner(modifier: Modifier = Modifier) {
    AndroidView(
        modifier = modifier,
        factory = { context ->
            AdView(context).apply {
                setAdSize(AdSize.BANNER)
                // Use test ad unit ID
                adUnitId = "ca-app-pub-3940256099942544/6300978111"
            }
        },
        update = { adView ->
            adView.loadAd(AdRequest.Builder().build())
        }
    )
}
- Tempatkan 
AdBannersebelumJuiceTrackerListdiJuiceTrackerApp.JuiceTrackerListdideklarasikan di baris 83. 
ui/JuiceTrackerApp.kt
...
AdBanner(
   Modifier
       .fillMaxWidth()
       .padding(
           top = dimensionResource(R.dimen.padding_medium),
           bottom = dimensionResource(R.dimen.padding_small)
       )
)
JuiceTrackerList(
    juices = trackerState,
    onDelete = { juice -> juiceTrackerViewModel.deleteJuice(juice) },
    onUpdate = { juice ->
        juiceTrackerViewModel.updateCurrentJuice(juice)
        scope.launch {
            bottomSheetScaffoldState.bottomSheetState.expand()
        }
     },
)
6. Mendapatkan kode solusi
Untuk mendownload kode codelab yang sudah selesai, Anda dapat menggunakan perintah git berikut:
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-juice-tracker.git $ cd basic-android-kotlin-compose-training-juice-tracker $ git checkout compose-with-views
Atau, Anda dapat mendownload repositori sebagai file ZIP, lalu mengekstraknya, dan membukanya di Android Studio.
Jika Anda ingin melihat kode solusi, lihat di GitHub.
7. Pelajari lebih lanjut
8. Selesai!
Kursus ini mungkin berakhir di sini, tetapi ini hanyalah awal dari perjalanan Anda dalam pengembangan aplikasi Android.
Dalam kursus ini, Anda telah mempelajari cara mem-build aplikasi menggunakan Jetpack Compose, toolkit UI modern untuk mem-build aplikasi Android native. Sepanjang kursus ini, Anda telah mem-build aplikasi dengan daftar, satu atau beberapa layar, dan menavigasi di antara layar. Anda telah mempelajari cara membuat aplikasi interaktif, membuat aplikasi merespons input pengguna, dan mengupdate UI. Anda telah menerapkan Desain Material dan menggunakan warna, bentuk, dan tipografi untuk tema aplikasi. Anda juga menggunakan Jetpack dan library pihak ketiga lainnya untuk menjadwalkan tugas, mengambil data dari server jarak jauh, mempertahankan data secara lokal, dan lainnya.
Dengan menyelesaikan kursus ini, Anda tidak hanya memiliki pemahaman yang baik tentang cara membuat aplikasi yang menarik dan responsif menggunakan Jetpack Compose, tetapi juga dibekali dengan pengetahuan dan keterampilan yang Anda butuhkan untuk membuat aplikasi Android yang efisien, mudah dikelola, dan menarik secara visual. Fondasi ini akan membantu Anda untuk terus belajar dan mengembangkan keterampilan dalam pengembangan Android Modern dan Compose.
Kami ingin berterima kasih kepada Anda semua karena telah berpartisipasi dan menyelesaikan kursus ini. Kami mendorong Anda semua untuk terus belajar dan mengembangkan keterampilan melalui berbagai referensi tambahan, seperti Dokumentasi Developer Android, Kursus Jetpack Compose untuk Android Developers, Arsitektur Aplikasi Android Modern, Blog Developer Android, codelab, dan project contoh lainnya.
Terakhir, jangan lupa untuk membagikan hashtag yang Anda buat di media sosial dan gunakan hashtag #AndroidBasics agar kami dan komunitas developer Android lainnya juga dapat mengikuti perjalanan pembelajaran Anda.
Selamat belajar menulis kode!!