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 disebutonColorChange
yang menggunakan parameterInt
.SpinnerAdapter
menangani 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 ke0
sehingga 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 parameterInt
untuk posisi indikator lingkaran berputar, fungsi callback yang menggunakan parameterInt
dan 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 tempatSpinner
muncul.
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
Spinner
di 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:
factory
lambda, yang merupakan fungsi untuk membuat View.- Callback
update
, yang dipanggil saat View yang dibuat difactory
di-inflate. modifier
composable.
- Untuk menerapkan
AndroidView
, mulailah dengan meneruskan pengubah dan mengisi lebar maksimum layar. - Teruskan lambda untuk parameter
factory
. - Lambda
factory
menggunakanContext
sebagai parameter. Buat classSpinner
lalu 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
.ArrayAdapter
memerlukan konteks, tata letak XML, dan array. Teruskansimple_spinner_dropdown_item
untuk 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
update
yang meneruskanspinner
. Gunakan callback yang disediakan diupdate
untuk 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
SpinnerAdapter
yang 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
ColorSpinnerRow
di ComposableSheetForm
pada 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
bottomsheet
yang bernamaRatingInputRow.kt
. - Dalam file
RatingInputRow.kt
, buat Composable baru yang bernamaRatingInputRow()
. - Dalam tanda tangan metode, teruskan
Int
untuk rating, callback dengan parameterInt
untuk menangani perubahan pemilihan, dan pengubah.
bottomsheet/RatingInputRow.kt
@Composable
fun RatingInputRow(rating:Int, onRatingChange: (Int) -> Unit, modifier: Modifier = Modifier){
}
- Seperti
ColorSpinnerRow
, tambahkanInputRow
ke 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 classRatingBar
yang menyediakan jenis batang rating yang diperlukan untuk desain ini. SetelstepSize
ke1f
untuk 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
RatingBar
di isi lambdaupdate
. - Saat rating baru ditetapkan, gunakan callback
RatingBar
untuk 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.BANNER
dan ID unit iklan ke"ca-app-pub-3940256099942544/6300978111"
. - Saat
AdView
di-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
AdBanner
sebelumJuiceTrackerList
diJuiceTrackerApp
.JuiceTrackerList
dideklarasikan 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. Mempelajari 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!!