1. Pengantar
Sistem Compose dan View dapat bekerja berdampingan.
Dalam codelab ini, Anda akan memigrasikan bagian layar detail tanaman Sunflower ke Compose. Kami membuat salinan project untuk Anda guna mencoba memigrasikan aplikasi yang realistis ke Compose.
Di akhir codelab, Anda dapat melanjutkan migrasi dan mengonversi layar Sunflower lainnya jika ingin.
Untuk mendapatkan dukungan lebih lanjut saat Anda mempelajari codelab ini, lihat kode berikut:
Yang akan Anda pelajari
Dalam codelab ini, Anda akan mempelajari:
- Berbagai jalur migrasi yang dapat Anda ikuti
- Cara memigrasikan aplikasi secara bertahap ke Compose
- Cara menambahkan Compose ke layar yang ada yang dibuat menggunakan tampilan Android
- Cara menggunakan Android View dari dalam Compose
- Cara menggunakan tema Anda dari sistem View di Compose
- Cara menguji layar dengan sistem View dan kode Compose
Prasyarat
- Pengalaman dengan sintaksis Kotlin, termasuk lambda
- Memahami dasar-dasar Compose
Yang akan Anda butuhkan
2. Merencanakan migrasi Anda
Bermigrasi ke Compose bergantung pada Anda dan tim Anda. Ada banyak cara untuk mengintegrasikan Jetpack Compose ke dalam aplikasi Android yang sudah ada. Dua strategi migrasi yang umum adalah:
- Mengembangkan layar baru sepenuhnya dengan Compose
- Mengambil layar yang ada, dan secara bertahap memigrasikan komponennya.
Compose di layar baru
Pendekatan umum saat memfaktorkan ulang aplikasi ke teknologi baru adalah mengadopsinya dalam fitur baru yang Anda build untuk aplikasi. Dalam hal ini, layar baru berlaku. Jika Anda perlu membuat layar UI baru untuk aplikasi, sebaiknya gunakan Compose untuk aplikasi tersebut saat bagian lain dari aplikasi mungkin tetap berada dalam sistem View.
Dalam hal ini, Anda akan melakukan interop Compose di tepi fitur yang dimigrasikan tersebut.
Compose dan View secara bersamaan
Dengan mempertimbangkan layar, Anda dapat memigrasikan beberapa bagian ke Compose dan bagian lainnya di sistem View. Misalnya, Anda dapat memigrasikan RecyclerView tanpa mengubah bagian layar lainnya di sistem View.
Atau sebaliknya, gunakan Compose sebagai tata letak luar dan gunakan beberapa tampilan yang sudah ada yang mungkin tidak tersedia di Compose seperti MapView atau AdView.
Menyelesaikan migrasi
Memigrasikan seluruh fragmen atau layar ke Compose satu per satu. Paling sederhana, tetapi sangat terperinci.
Dan di codelab ini?
Dalam codelab ini, Anda akan melakukan migrasi inkremental ke Compose di layar detail tanaman Sunflower yang membuat Compose dan View dapat bekerja sama. Setelah itu, Anda akan memiliki cukup pengetahuan untuk melanjutkan migrasi jika Anda ingin.
3 Mempersiapkan
Mendapatkan kode
Dapatkan kode codelab dari GitHub:
$ git clone https://github.com/googlecodelabs/android-compose-codelabs
Atau, Anda dapat mendownload repositori sebagai file Zip:
Membuka Android Studio
Codelab ini memerlukan Android Studio Bumblebee.
Menjalankan aplikasi contoh
Kode yang baru saja Anda download berisi kode untuk semua codelab Compose yang tersedia. Untuk menyelesaikan codelab ini, buka project MigrationCodelab
di dalam Android Studio.
Dalam codelab ini, Anda akan memigrasikan layar detail tanaman Sunflower ke Compose. Anda dapat membuka layar detail tanaman dengan mengetuk salah satu tanaman yang tersedia di layar daftar tanaman.
Penyiapan project
Project ini dibuat di beberapa cabang git:
main
adalah cabang yang Anda buka atau download. Ini adalah titik awal codelab.end
berisi solusi untuk codelab ini.
Sebaiknya Anda memulai dengan kode di cabang main
dan mengikuti codelab langkah demi langkah sesuai kemampuan Anda.
Selama codelab, Anda akan melihat cuplikan kode yang harus ditambahkan ke project. Di beberapa tempat, Anda juga harus menghapus kode yang disebutkan secara eksplisit dalam komentar pada cuplikan kode.
Untuk mendapatkan cabang end
menggunakan git, gunakan perintah ini:
$ git clone -b end https://github.com/googlecodelabs/android-compose-codelabs
Atau download kode solusi dari sini:
Pertanyaan umum (FAQ)
4. Compose di Sunflower
Compose telah ditambahkan ke kode yang Anda download dari cabang main
. Namun, mari kita lihat apa yang diperlukan agar berfungsi.
Jika Anda membuka file app/build.gradle
(atau build.gradle (Module: compose-migration.app)
), lihat cara mengimpor dependensi Compose dan memungkinkan Android Studio berfungsi dengan Compose menggunakan flag buildFeatures { compose true }
.
app/build.gradle
android {
...
kotlinOptions {
jvmTarget = '1.8'
useIR = true
}
buildFeatures {
...
compose true
}
composeOptions {
kotlinCompilerExtensionVersion rootProject.composeVersion
}
}
dependencies {
...
// Compose
implementation "androidx.compose.runtime:runtime:$rootProject.composeVersion"
implementation "androidx.compose.ui:ui:$rootProject.composeVersion"
implementation "androidx.compose.foundation:foundation:$rootProject.composeVersion"
implementation "androidx.compose.foundation:foundation-layout:$rootProject.composeVersion"
implementation "androidx.compose.material:material:$rootProject.composeVersion"
implementation "androidx.compose.runtime:runtime-livedata:$rootProject.composeVersion"
implementation "androidx.compose.ui:ui-tooling:$rootProject.composeVersion"
implementation "com.google.android.material:compose-theme-adapter:$rootProject.composeVersion"
...
}
Versi dependensi tersebut ditetapkan dalam file build.gradle
root.
5. Halo Compose!
Di layar detail tanaman, kita akan memigrasikan deskripsi tanaman ke Compose tanpa mengubah keseluruhan struktur layar. Di sini, Anda akan mengikuti strategi migrasi Compose dan View secara bersamaan yang disebutkan di bagian Merencanakan migrasi.
Compose memerlukan Activity atau Fragment host untuk merender UI. Di Sunflower, karena semua layar menggunakan fragmen, Anda akan menggunakan ComposeView
: Android View yang dapat menghosting konten UI Compose menggunakan metode setContent
.
Menghapus kode XML
Mari mulai dengan migrasi! Buka fragment_plant_detail.xml
, lalu lakukan hal berikut ini:
- Beralih ke Tampilan kode
- Hapus kode
ConstraintLayout
danTextView
bertingkat di dalamNestedScrollView
(codelab akan membandingkan dan mereferensikan kode XML saat memigrasikan setiap item, menjadikan kode sebagai komentar akan berguna) - Tambahkan
ComposeView
yang akan menghosting kode Compose dengancompose_view
sebagai ID tampilan
fragment_plant_detail.xml
<androidx.core.widget.NestedScrollView
android:id="@+id/plant_detail_scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="@dimen/fab_bottom_padding"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
// Step 2) Comment out ConstraintLayout and its children
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="@dimen/margin_normal">
<TextView
android:id="@+id/plant_detail_name"
...
</androidx.constraintlayout.widget.ConstraintLayout>
// End Step 2) Comment out until here
// Step 3) Add a ComposeView to host Compose code
<androidx.compose.ui.platform.ComposeView
android:id="@+id/compose_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.core.widget.NestedScrollView>
Menambahkan kode Compose
Pada tahap ini, Anda siap untuk mulai memigrasikan layar detail tanaman ke Compose!
Di seluruh codelab, Anda akan menambahkan kode Compose ke file PlantDetailDescription.kt
di folder plantdetail
. Buka dan lihat bagaimana kita memiliki teks "Hello Compose!"
placeholder yang sudah tersedia dalam project.
plantdetail/PlantDetailDescription.kt
@Composable
fun PlantDetailDescription() {
Text("Hello Compose")
}
Mari kita tampilkan ini di layar dengan memanggil composable ini dari ComposeView
yang kita tambahkan pada langkah sebelumnya. Buka plantdetail/PlantDetailFragment.kt
.
Karena layar menggunakan data binding, Anda dapat langsung mengakses composeView
dan memanggil setContent
untuk menampilkan kode Compose di layar. Panggil composable PlantDetailDescription
di dalam MaterialTheme
karena Sunflower menggunakan desain material.
plantdetail/PlantDetailFragment.kt
class PlantDetailFragment : Fragment() {
...
override fun onCreateView(...): View? {
val binding = DataBindingUtil.inflate<FragmentPlantDetailBinding>(
inflater, R.layout.fragment_plant_detail, container, false
).apply {
...
composeView.setContent {
// You're in Compose world!
MaterialTheme {
PlantDetailDescription()
}
}
}
...
}
}
Jika menjalankan aplikasi, Anda akan melihat "Hello Compose!
" ditampilkan di layar.
6. Membuat Composable dari XML
Mari mulai dengan memigrasikan nama tanaman. Lebih tepatnya, TextView
dengan ID @+id/plant_detail_name
yang Anda hapus di fragment_plant_detail.xml
. Berikut adalah kode XML:
<TextView
android:id="@+id/plant_detail_name"
...
android:layout_marginStart="@dimen/margin_small"
android:layout_marginEnd="@dimen/margin_small"
android:gravity="center_horizontal"
android:text="@{viewModel.plant.name}"
android:textAppearance="?attr/textAppearanceHeadline5"
... />
Lihat bagaimana hasilnya memiliki gaya textAppearanceHeadline5
, memiliki margin horizontal 8.dp
, dan dipusatkan secara horizontal di layar. Namun, judul yang akan ditampilkan diamati dari LiveData
yang diekspos oleh PlantDetailViewModel
yang berasal dari lapisan repositori.
Karena mengamati LiveData
akan dibahas nanti, anggaplah kita memiliki nama yang tersedia dan diteruskan sebagai parameter ke composable PlantName
baru yang kita buat di file PlantDetailDescription.kt
. Composable ini nantinya akan dipanggil dari composable PlantDetailDescription
.
PlantDetailDescription.kt
@Composable
private fun PlantName(name: String) {
Text(
text = name,
style = MaterialTheme.typography.h5,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = dimensionResource(R.dimen.margin_small))
.wrapContentWidth(Alignment.CenterHorizontally)
)
}
@Preview
@Composable
private fun PlantNamePreview() {
MaterialTheme {
PlantName("Apple")
}
}
Dengan pratinjau:
Dalam hal ini:
- Gaya
Text
adalahMaterialTheme.typography.h5
yang memetakan ketextAppearanceHeadline5
dari kode XML. - Pengubah mendekorasi Teks untuk menyesuaikannya agar terlihat seperti versi XML:
- Pengubah
fillMaxWidth
sesuai denganandroid:layout_width="match_parent"
dalam kode XML. padding
horizontal darimargin_small
yang merupakan nilai dari sistem View menggunakan fungsi bantuandimensionResource
.wrapContentWidth
untuk meratakanText
secara horizontal.
7. ViewModels dan LiveData
Sekarang, mari kita hubungkan judul ke layar. Untuk melakukannya, Anda perlu memuat data menggunakan PlantDetailViewModel
. Untuk itu, Compose dilengkapi dengan integrasi untuk ViewModel dan LiveData.
ViewModels
Sebagai instance PlantDetailViewModel
yang digunakan dalam Fragment, kita dapat meneruskannya sebagai parameter ke PlantDetailDescription
dan selesai.
Buka file PlantDetailDescription.kt
dan tambahkan parameter PlantDetailViewModel
ke PlantDetailDescription
:
PlantDetailDescription.kt
@Composable
fun PlantDetailDescription(plantDetailViewModel: PlantDetailViewModel) {
...
}
Sekarang, teruskan instance ViewModel saat memanggil composable ini dari fragmen:
PlantDetailFragment.kt
class PlantDetailFragment : Fragment() {
...
override fun onCreateView(...): View? {
...
composeView.setContent {
MaterialTheme {
PlantDetailDescription(plantDetailViewModel)
}
}
}
}
LiveData
Dengan ini, Anda sudah memiliki akses ke kolom LiveData<Plant>
dari PlantDetailViewModel
untuk mendapatkan nama tanaman.
Untuk mengamati LiveData dari composable, gunakan fungsi LiveData.observeAsState()
.
Karena nilai yang dimunculkan oleh LiveData bisa bernilai null, Anda harus menggabungkan penggunaannya dalam pemeriksaan null. Karena itu dan untuk tujuan penggunaan kembali, merupakan pola yang bagus untuk membagi penggunaan LiveData dan memantau berbagai composable. Dengan demikian, buat composable baru bernama PlantDetailContent
yang akan menampilkan informasi Plant
.
Dengan begitu, ini adalah tampilan file PlantDetailDescription.kt
setelah menambahkan pengamatan LiveData.
PlantDetailDescription.kt
@Composable
fun PlantDetailDescription(plantDetailViewModel: PlantDetailViewModel) {
// Observes values coming from the VM's LiveData<Plant> field
val plant by plantDetailViewModel.plant.observeAsState()
// If plant is not null, display the content
plant?.let {
PlantDetailContent(it)
}
}
@Composable
fun PlantDetailContent(plant: Plant) {
PlantName(plant.name)
}
@Preview
@Composable
private fun PlantDetailContentPreview() {
val plant = Plant("id", "Apple", "description", 3, 30, "")
MaterialTheme {
PlantDetailContent(plant)
}
}
Dengan pratinjau yang sama seperti PlantNamePreview
karena saat ini PlantDetailContent
hanya memanggil PlantName
:
Sekarang, Anda telah menghubungkan ViewModel sepenuhnya untuk menampilkan nama tanaman dalam Compose. Dalam beberapa bagian berikutnya, Anda akan membuat komponen lainnya dan menghubungkannya ke ViewModel dengan cara yang sama.
8. Migrasi kode XML lainnya
Sekarang, lebih mudah untuk menyelesaikan apa yang kurang di UI kita: info penyiraman dan deskripsi tanaman. Dengan mengikuti pendekatan kode XML yang sama dengan yang Anda lakukan sebelumnya, Anda sudah dapat memigrasikan bagian layar lainnya.
Kode XML info penyiraman yang dihapus sebelumnya dari fragment_plant_detail.xml
terdiri dari dua TextView dengan ID plant_watering_header
dan plant_watering
.
<TextView
android:id="@+id/plant_watering_header"
...
android:layout_marginStart="@dimen/margin_small"
android:layout_marginTop="@dimen/margin_normal"
android:layout_marginEnd="@dimen/margin_small"
android:gravity="center_horizontal"
android:text="@string/watering_needs_prefix"
android:textColor="?attr/colorAccent"
android:textStyle="bold"
... />
<TextView
android:id="@+id/plant_watering"
...
android:layout_marginStart="@dimen/margin_small"
android:layout_marginEnd="@dimen/margin_small"
android:gravity="center_horizontal"
app:wateringText="@{viewModel.plant.wateringInterval}"
.../>
Sama seperti yang Anda lakukan sebelumnya, buat composable baru bernama PlantWatering
lalu tambahkan Text
untuk menampilkan informasi penyiraman di layar:
PlantDetailDescription.kt
@Composable
private fun PlantWatering(wateringInterval: Int) {
Column(Modifier.fillMaxWidth()) {
// Same modifier used by both Texts
val centerWithPaddingModifier = Modifier
.padding(horizontal = dimensionResource(R.dimen.margin_small))
.align(Alignment.CenterHorizontally)
val normalPadding = dimensionResource(R.dimen.margin_normal)
Text(
text = stringResource(R.string.watering_needs_prefix),
color = MaterialTheme.colors.primaryVariant,
fontWeight = FontWeight.Bold,
modifier = centerWithPaddingModifier.padding(top = normalPadding)
)
val wateringIntervalText = LocalContext.current.resources.getQuantityString(
R.plurals.watering_needs_suffix, wateringInterval, wateringInterval
)
Text(
text = wateringIntervalText,
modifier = centerWithPaddingModifier.padding(bottom = normalPadding)
)
}
}
@Preview
@Composable
private fun PlantWateringPreview() {
MaterialTheme {
PlantWatering(7)
}
}
Dengan pratinjau:
Beberapa hal yang perlu diperhatikan:
- Karena padding horizontal dan dekorasi perataan digunakan bersama oleh composable
Text
, Anda dapat menggunakan kembali Pengubah dengan menetapkannya ke variabel lokal (yaitucenterWithPaddingModifier
). Anda dapat melakukannya karena pengubah adalah objek Kotlin reguler. MaterialTheme
Compose tidak memiliki kecocokan yang sama persis dengancolorAccent
yang digunakan diplant_watering_header
. Untuk saat ini, mari gunakanMaterialTheme.colors.primaryVariant
yang akan Anda tingkatkan di bagian tema.
Mari hubungkan semua bagian dan memanggil PlantWatering
dari PlantDetailContent
juga. Kode XML ConstraintLayout yang kita hapus di awal memiliki margin 16.dp
yang perlu disertakan dalam kode Compose.
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="@dimen/margin_normal">
Di PlantDetailContent
, buat Column
untuk menampilkan nama dan info penyiraman sekaligus dan membuatnya sebagai padding. Selain itu, agar warna latar belakang dan warna teks yang digunakan sesuai, tambahkan Surface
yang akan menanganinya.
PlantDetailDescription.kt
@Composable
fun PlantDetailContent(plant: Plant) {
Surface {
Column(Modifier.padding(dimensionResource(R.dimen.margin_normal))) {
PlantName(plant.name)
PlantWatering(plant.wateringInterval)
}
}
}
Jika memuat ulang pratinjau, Anda akan melihat:
9. View di kode Compose
Sekarang, mari migrasikan deskripsi tanaman. Kode dalam fragment_plant_detail.xml
memiliki TextView
dengan app:renderHtml="@{viewModel.plant.description}"
untuk memberi tahu XML teks apa yang ditampilkan di layar. renderHtml
adalah adaptor binding yang dapat Anda temukan di file PlantDetailBindingAdapters.kt
. Implementasi menggunakan HtmlCompat.fromHtml
untuk menetapkan teks di TextView
!
Namun, saat ini Compose tidak memiliki dukungan untuk class Spanned
atau menampilkan teks berformat HTML. Dengan demikian, kita perlu menggunakan TextView
dari sistem View dalam kode Compose untuk mengabaikan batasan ini.
Karena Compose belum dapat merender kode HTML, Anda akan membuat TextView
secara terprogram untuk melakukan hal tersebut menggunakan AndroidView
API.
AndroidView
menganggap View
sebagai parameter dan memberi Anda callback saat View telah di-inflate.
Mari lakukan ini dengan membuat composable PlantDescription
baru. Composable ini memanggil AndroidView
dengan TextView
yang baru saja kita ingat di lambda. Dalam callback factory
, lakukan inisialisasi TextView
yang bereaksi pada interaksi HTML menggunakan Context
yang diberikan. Dalam callback update
juga, tetapkan teks dengan deskripsi berformat HTML yang diingat.
PlantDetailDescription.kt
@Composable
private fun PlantDescription(description: String) {
// Remembers the HTML formatted description. Re-executes on a new description
val htmlDescription = remember(description) {
HtmlCompat.fromHtml(description, HtmlCompat.FROM_HTML_MODE_COMPACT)
}
// Displays the TextView on the screen and updates with the HTML description when inflated
// Updates to htmlDescription will make AndroidView recompose and update the text
AndroidView(
factory = { context ->
TextView(context).apply {
movementMethod = LinkMovementMethod.getInstance()
}
},
update = {
it.text = htmlDescription
}
)
}
@Preview
@Composable
private fun PlantDescriptionPreview() {
MaterialTheme {
PlantDescription("HTML<br><br>description")
}
}
Pratinjau:
Perhatikan bahwa htmlDescription
mengingat deskripsi HTML untuk description
yang diberikan yang diteruskan sebagai parameter. Jika parameter description
berubah, kode htmlDescription
di dalam remember
akan dijalankan kembali.
Demikian pula, callback update AndroidView
akan direkomposisi jika htmlDescription
berubah. Setiap status yang dibaca di dalam callback menyebabkan rekomposisi.
Mari tambahkan PlantDescription
ke composable PlantDetailContent
dan ubah kode pratinjau untuk menampilkan deskripsi HTML juga:
PlantDetailDescription.kt
@Composable
fun PlantDetailContent(plant: Plant) {
Surface {
Column(Modifier.padding(dimensionResource(R.dimen.margin_normal))) {
PlantName(plant.name)
PlantWatering(plant.wateringInterval)
PlantDescription(plant.description)
}
}
}
@Preview
@Composable
private fun PlantDetailContentPreview() {
val plant = Plant("id", "Apple", "HTML<br><br>description", 3, 30, "")
MaterialTheme {
PlantDetailContent(plant)
}
}
Dengan pratinjau:
Pada tahap ini, Anda telah memigrasikan semua konten dalam ConstraintLayout
asli ke Compose. Anda dapat menjalankan aplikasi untuk memastikan aplikasi bekerja seperti yang diharapkan.
10. ViewCompositionStrategy
Secara default, Compose akan menghapus Komposisi setiap kali ComposeView
dilepas dari jendela. Hal ini tidak diinginkan jika ComposeView
digunakan dalam fragmen karena beberapa alasan:
- Komposisi harus mengikuti siklus proses tampilan fragmen untuk jenis
View
UI Compose untuk menyimpan status, dan - untuk mempertahankan elemen UI Compose di layar saat transisi, atau transisi jendela, terjadi. Selama transisi,
ComposeView
itu sendiri tetap terlihat bahkan setelah dilepas dari jendela.
Anda dapat memanggil metode AbstractComposeView.disposeComposition
secara manual untuk membuang Komposisi secara manual. Atau, untuk menghapus Komposisi secara otomatis saat tidak diperlukan lagi, tetapkan strategi yang berbeda atau buat strategi sendiri dengan memanggil metode setViewCompositionStrategy
.
Gunakan strategi DisposeOnViewTreeLifecycleDestroyed
untuk membuang Komposisi saat LifecycleOwner
fragmen dihancurkan.
Karena PlantDetailFragment
memiliki transisi masuk dan keluar (lihat nav_garden.xml
untuk info selengkapnya), dan kita akan menggunakan jenis View
di dalam Compose nanti, kita perlu memastikan ComposeView
menggunakan strategi DisposeOnViewTreeLifecycleDestroyed
. Meskipun demikian, praktik yang baik adalah untuk selalu menetapkan strategi ini saat menggunakan ComposeView
dalam fragmen.
plantdetail/PlantDetailFragment.kt
import androidx.compose.ui.platform.ViewCompositionStrategy
...
class PlantDetailFragment : Fragment() {
...
override fun onCreateView(...): View? {
val binding = DataBindingUtil.inflate<FragmentPlantDetailBinding>(
inflater, R.layout.fragment_plant_detail, container, false
).apply {
...
composeView.apply {
// Dispose the Composition when the view's LifecycleOwner
// is destroyed
setViewCompositionStrategy(
ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
)
setContent {
MaterialTheme {
PlantDetailDescription(plantDetailViewModel)
}
}
}
}
...
}
}
11. Tema interop
Kita memiliki konten teks detail tanaman yang dimigrasikan ke Compose. Namun, Anda mungkin melihat bahwa Compose tidak menggunakan warna tema yang tepat. Compose menggunakan warna ungu untuk nama tanaman, yang seharusnya berwarna hijau.
Pada tahap migrasi awal ini, Anda mungkin ingin Compose menggunakan kembali tema yang tersedia di sistem View, bukan menulis ulang tema Material Anda sendiri di Compose dari awal. Tema Material berfungsi dengan baik dengan semua komponen Desain material yang disertakan dengan Compose.
Untuk menggunakan kembali tema Komponen Desain Material (MDC) sistem View di Compose, Anda dapat menggunakan compose-theme-adapter. Fungsi MdcTheme
akan otomatis membaca tema MDC konteks host dan meneruskannya ke MaterialTheme
atas nama Anda untuk tema terang dan gelap. Meskipun Anda hanya memerlukan warna tema untuk codelab ini, library ini juga membaca bentuk dan tipografi sistem View.
Library sudah disertakan dalam file app/build.gradle
sebagai berikut:
...
dependencies {
...
implementation "com.google.android.material:compose-theme-adapter:$rootProject.composeVersion"
...
}
Untuk menggunakannya, ganti penggunaan MaterialTheme
untuk MdcTheme
. Misalnya, dalam PlantDetailFragment
:
PlantDetailFragment.kt
class PlantDetailFragment : Fragment() {
...
composeView.apply {
...
setContent {
MdcTheme {
PlantDetailDescription(plantDetailViewModel)
}
}
}
}
Dan semua composable pratinjau dalam file PlantDetailDescription.kt
:
PlantDetailDescription.kt
@Preview
@Composable
private fun PlantDetailContentPreview() {
val plant = Plant("id", "Apple", "HTML<br><br>description", 3, 30, "")
MdcTheme {
PlantDetailContent(plant)
}
}
@Preview
@Composable
private fun PlantNamePreview() {
MdcTheme {
PlantName("Apple")
}
}
@Preview
@Composable
private fun PlantWateringPreview() {
MdcTheme {
PlantWatering(7)
}
}
@Preview
@Composable
private fun PlantDescriptionPreview() {
MdcTheme {
PlantDescription("HTML<br><br>description")
}
}
Seperti yang dapat Anda lihat di pratinjau, MdcTheme
mengambil warna dari tema di file styles.xml
.
Anda juga dapat melihat pratinjau UI dalam tema gelap dengan membuat fungsi baru dan meneruskan Configuration.UI_MODE_NIGHT_YES
ke uiMode
dari pratinjau:
import android.content.res.Configuration
...
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun PlantDetailContentDarkPreview() {
val plant = Plant("id", "Apple", "HTML<br><br>description", 3, 30, "")
MdcTheme {
PlantDetailContent(plant)
}
}
Dengan pratinjau:
Jika Anda menjalankan aplikasi, aplikasi akan berperilaku sama persis sebelum migrasi dalam tema terang dan gelap:
12. Pengujian
Setelah memigrasikan bagian layar detail tanaman ke Compose, pengujian sangat penting dilakukan untuk memastikan Anda tidak merusak apa pun.
Di Sunflower, PlantDetailFragmentTest
yang terletak di folder androidTest
menguji beberapa fungsi aplikasi. Buka file dan lihat kode saat ini:
testPlantName
memeriksa nama tanaman di layartestShareTextIntent
memeriksa apakah intent yang benar dipicu setelah mengetuk tombol bagikan
Saat aktivitas atau fragmen menggunakan compose, daripada menggunakan ActivityScenarioRule
, Anda harus menggunakan createAndroidComposeRule
yang mengintegrasikan ActivityScenarioRule
dengan ComposeTestRule
yang memungkinkan Anda menguji kode Compose.
Di PlantDetailFragmentTest
, ganti ActivityScenarioRule
penggunaan dengan createAndroidComposeRule
. Saat aturan aktivitas diperlukan untuk mengonfigurasi pengujian, gunakan atribut activityRule
dari createAndroidComposeRule
sebagai berikut:
@RunWith(AndroidJUnit4::class)
class PlantDetailFragmentTest {
@Rule
@JvmField
val composeTestRule = createAndroidComposeRule<GardenActivity>()
...
@Before
fun jumpToPlantDetailFragment() {
populateDatabase()
composeTestRule.activityRule.scenario.onActivity { gardenActivity ->
activity = gardenActivity
val bundle = Bundle().apply { putString("plantId", "malus-pumila") }
findNavController(activity, R.id.nav_host).navigate(R.id.plant_detail_fragment, bundle)
}
}
...
}
Jika Anda menjalankan pengujian, testPlantName
akan gagal! testPlantName
memeriksa apakah TextView berada di layar. Namun, Anda telah memigrasikan bagian UI tersebut ke Compose. Dengan demikian, Anda perlu menggunakan pernyataan Compose:
@Test
fun testPlantName() {
composeTestRule.onNodeWithText("Apple").assertIsDisplayed()
}
Jika Anda menjalankan pengujian, Anda akan melihat semuanya lolos.
13. Selamat
Selamat, Anda berhasil menyelesaikan codelab ini.
Cabang compose
dari project github Sunflower asli memigrasikan layar detail tanaman sepenuhnya ke Compose. Selain dari yang telah Anda kerjakan di codelab ini, codelab juga menyimulasikan perilaku CollapsingToolbarLayout. Hal ini meliputi:
- Memuat gambar dengan Compose
- Animasi
- Penanganan dimensi yang lebih baik
- Dan banyak lagi!
Apa selanjutnya?
Lihat codelab lain di jalur Compose:
Bacaan lebih lanjut
- Kode migrasi Jetpack Compose
- Panduan Compose di aplikasi yang sudah ada
- Crane, aplikasi contoh, menyematkan MapView di Compose