Jetpack Compose dirancang untuk bekerja dengan pendekatan UI yang sudah ada berbasis tampilan. Jika Anda membuat aplikasi baru, opsi terbaik adalah menerapkan seluruh UI Anda dengan Compose. Namun, jika Anda memodifikasi aplikasi yang sudah ada, Anda mungkin tidak ingin sepenuhnya memigrasikan aplikasi sekaligus. Sebagai gantinya, Anda dapat menggabungkan Compose dengan penerapan desain UI yang sudah ada.
Mengadopsi Compose di aplikasi Anda
Ada dua cara utama untuk mengintegrasikan Compose dengan UI berbasis tampilan:
Anda dapat menambahkan elemen Compose ke UI yang sudah ada dengan membuat layar baru berbasis Compose, atau dengan menambahkan elemen Compose ke aktivitas, fragmen atau tata letak tampilan yang sudah ada.
Anda dapat menambahkan elemen UI berbasis tampilan ke dalam fungsi yang dapat dikomposisi. Dengan melakukannya, Anda dapat menambahkan tampilan Android ke dalam desain berbasis Compose.
Cara terbaik untuk memigrasikan seluruh aplikasi ke Compose adalah dengan membuat perincian yang dibutuhkan project secara bertahap. Anda dapat memigrasikan satu layar dalam satu waktu, atau bahkan satu fragmen atau elemen UI lain yang dapat digunakan kembali dalam satu waktu. Anda dapat menggunakan beberapa pendekatan yang berbeda:
Pendekatan bottom-up mulai memigrasikan elemen UI yang lebih kecil di layar, seperti
Button
atauTextView
, diikuti dengan elemenViewGroup
sampai semuanya dikonversi menjadi fungsi yang dapat dikomposisi.Pendekatan vertikal mulai memigrasikan fragmen atau container tampilan, seperti
FrameLayout
,ConstraintLayout
, atauRecyclerView
, diikuti dengan elemen UI yang lebih kecil di layar.
Pendekatan ini mengasumsikan bahwa setiap layar bersifat mandiri, tetapi Anda juga dapat memigrasikan UI bersama, seperti sistem desain, ke Jetpack Compose. Lihat Memigrasikan UI bersama di bawah untuk informasi selengkapnya.
Interoperabilitas API
Saat mengadopsi Compose di aplikasi Anda, UI berbasis tampilan dan UI Compose dapat digabungkan. Berikut adalah daftar API, rekomendasi, dan tips untuk mempermudah transisi ke Compose.
Compose di Android View
Anda dapat menambahkan UI berbasis Compose ke dalam aplikasi yang sudah ada dan menggunakan desain berbasis tampilan.
Untuk membuat layar baru yang sepenuhnya berbasis Compose, minta
aktivitas Anda memanggil metode setContent()
, dan teruskan fungsi mana pun yang dapat dikomposisi,
yang Anda suka.
class ExampleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { // In here, we can call composables!
MaterialTheme {
Greeting(name = "compose")
}
}
}
}
@Composable
fun Greeting(name: String) {
Text(text = "Hello $name!")
}
Kode ini terlihat seperti yang Anda temukan di aplikasi khusus Compose.
Jika Anda ingin menggabungkan konten UI Compose dalam fragmen atau tata letak
View yang sudah ada, gunakan ComposeView
dan panggil
metode
setContent()
. ComposeView
adalah View
Android.
Anda harus memasang ComposeView
ke
ViewTreeLifecycleOwner
.
ViewTreeLifecycleOwner
memungkinkan tampilan untuk dipasang dan dilepas
berulang kali sambil mempertahankan komposisi. ComponentActivity
,
FragmentActivity
, dan AppCompatActivity
adalah contoh class yang
menerapkan ViewTreeLifecycleOwner
.
Anda dapat menempatkan ComposeView
di tata letak XML, seperti View
lainnya:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/hello_world"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello Android!" />
<androidx.compose.ui.platform.ComposeView
android:id="@+id/compose_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Dalam kode sumber Kotlin, inflate tata letak dari resource
tata letak yang ditentukan dalam XML. Kemudian, dapatkan
ComposeView
menggunakan ID XML, dan panggil setContent()
untuk menggunakan Compose.
class ExampleFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate the layout for this fragment
return inflater.inflate(
R.layout.fragment_example, container, false
).apply {
findViewById<ComposeView>(R.id.compose_view).setContent {
// In Compose world
MaterialTheme {
Text("Hello Compose!")
}
}
}
}
}
Gambar 1. Ini menunjukkan output kode yang menambahkan elemen Compose dalam
hierarki UI View. Teks "Hello Android!" ditampilkan oleh
widget TextView
. Teks "Hello Compose!" ditampilkan oleh
elemen teks Compose.
Anda juga dapat menyertakan ComposeView
secara langsung dalam fragmen jika layar penuh Anda
dibuat dengan Compose, yang memungkinkan Anda menghindari penggunaan file tata letak XML sepenuhnya.
class ExampleFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setContent {
MaterialTheme {
// In Compose world
Text("Hello Compose!")
}
}
}
}
}
Jika ada beberapa elemen ComposeView
dalam tata letak yang sama, masing-masing harus
memiliki ID unik agar savedInstanceState
berfungsi. Baca informasi selengkapnya
tentang hal ini di bagian SavedInstanceState
.
class ExampleFragment : Fragment() {
override fun onCreateView(...): View = LinearLayout(...).apply {
addView(ComposeView(...).apply {
id = R.id.compose_view_x
...
})
addView(TextView(...))
addView(ComposeView(...).apply {
id = R.id.compose_view_y
...
})
}
}
}
ID ComposeView
ditentukan dalam file res/values/ids.xml
:
<resources>
<item name="compose_view_x" type="id" />
<item name="compose_view_y" type="id" />
</resources>
Android View di Compose
Anda bisa menyertakan hierarki Android View di UI Compose. Pendekatan ini
sangat berguna jika Anda ingin menggunakan elemen UI yang belum tersedia di
Compose, seperti
AdView
atau
MapView
.
Pendekatan ini juga memungkinkan Anda menggunakan kembali tampilan kustom yang mungkin sudah Anda desain.
Untuk menyertakan elemen tampilan atau hierarki, gunakan komponen
AndroidView
.
AndroidView
diteruskan lambda yang menampilkan
View
. AndroidView
juga menyediakan callback
update
yang dipanggil saat tampilan di-inflate. AndroidView
akan merekomposisi
setiap kali pembacaan State
dalam callback berubah.
@Composable
fun CustomView() {
val selectedItem = remember { mutableStateOf(0) }
// Adds view to Compose
AndroidView(
modifier = Modifier.fillMaxSize(), // Occupy the max size in the Compose UI tree
factory = { context ->
// Creates custom view
CustomView(context).apply {
// Sets up listeners for View -> Compose communication
myView.setOnClickListener {
selectedItem.value = 1
}
}
},
update = { view ->
// View's been inflated or state read in this block has been updated
// Add logic here if necessary
// As selectedItem is read here, AndroidView will recompose
// whenever the state changes
// Example of Compose -> View communication
view.coordinator.selectedItem = selectedItem.value
}
)
}
@Composable
fun ContentExample() {
Column(Modifier.fillMaxSize()) {
Text("Look at this CustomView!")
CustomView()
}
}
Untuk menyematkan tata letak XML, gunakan
API
AndroidViewBinding
, yang disediakan oleh library androidx.compose.ui:ui-viewbinding
. Untuk
melakukannya, project Anda harus mengaktifkan binding tampilan.
AndroidView
, seperti banyak fungsi bawaan lainnya, memerlukan parameter Modifier
yang dapat digunakan, misalnya, untuk menyetel posisinya di komponen
induk.
@Composable
fun AndroidViewBindingExample() {
AndroidViewBinding(ExampleLayoutBinding::inflate) {
exampleView.setBackgroundColor(Color.GRAY)
}
}
Memanggil framework Android dari Compose
Compose terikat erat dengan class framework Android. Misalnya, class
tersebut dihosting di class Android View, seperti Activity
atau Fragment
, dan mungkin perlu
menggunakan class framework Android seperti Context
, resource sistem,
Service
, atau BroadcastReceiver
.
Untuk mempelajari resource sistem lebih lanjut, lihat dokumentasi Resource dalam Compose.
Komposisi Lokal
Dengan class CompositionLocal
,
penerusan data secara implisit bisa dilakukan melalui fungsi yang dapat dikomposisi. Fungsi ini
biasanya diberi nilai dalam node tertentu dari pohon UI. Nilai tersebut dapat
digunakan oleh turunannya yang dapat disusun tanpa mendeklarasikan CompositionLocal
sebagai parameter dalam fungsi yang dapat dikomposisi.
CompositionLocal
digunakan untuk menyebarkan nilai jenis framework Android di
Compose sepertiContext
,Configuration
atauView
di mana kode Compose
dihosting dengan kode yang sesuai
LocalContext
,
LocalConfiguration
,
atau
LocalView
.
Perhatikan bahwa class CompositionLocal
diawali dengan Local
agar mendapatkan visibilitas yang lebih baik
dengan pelengkapan otomatis di IDE.
Akses nilai CompositionLocal
saat ini menggunakan properti
current
. Misalnya, kode di bawah membuat tampilan kustom menggunakan Context
yang tersedia di bagian pohon UI Compose dengan memanggil LocalContext.current
.
@Composable
fun rememberCustomView(): CustomView {
val context = LocalContext.current
return remember { CustomView(context).apply { /*...*/ } }
}
Untuk contoh yang lebih lengkap, lihat bagian Studi Kasus: BroadcastReceivers di akhir dokumen ini.
Interaksi lainnya
Jika tidak ada utilitas yang ditetapkan untuk interaksi yang Anda butuhkan, praktik terbaiknya adalah mengikuti pedoman Compose umum, data mengalir ke bawah, peristiwa mengalir ke atas (dibahas lebih jauh lagi di bagian Berpikir dalam Compose). Misalnya, komponen ini akan meluncurkan aktivitas yang berbeda:
class ExampleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// get data from savedInstanceState
setContent {
MaterialTheme {
ExampleComposable(data, onButtonClick = {
startActivity(/*...*/)
})
}
}
}
}
@Composable
fun ExampleComposable(data: DataExample, onButtonClick: () -> Unit) {
Button(onClick = onButtonClick) {
Text(data.title)
}
}
Integrasi dengan library umum
Untuk melihat bagaimana Compose terintegrasi dengan library
umum seperti ViewModel
, Flow
,
Paging
, atau
Hilt
, lihat Panduan integrasi Compose
dengan library umum.
Tema
Mengikuti Desain Material, menggunakan library
Komponen Desain Material untuk
Android (MDC)
adalah cara yang disarankan untuk tema aplikasi Android. Seperti yang tercakup dalam
dokumentasi Compose Theming, Compose menerapkan
konsep ini dengan
komponen
MaterialTheme
.
Saat membuat layar baru di Compose, Anda harus memastikan bahwa Anda telah menerapkan
MaterialTheme
sebelum komponen yang membuat UI dari library komponen
material. Komponen material (Button
, Text
, dll.) bergantung pada
MaterialTheme
yang ada dan perilakunya tidak ditentukan tanpanya.
Semua sampel Jetpack Compose menggunakan
tema Compose khusus yang dibuat berdasarkan
MaterialTheme
.
Beberapa sumber kebenaran
Aplikasi yang ada cenderung memiliki tema dan gaya visual yang banyak untuk
ditampilkan. Saat memperkenalkan Compose di aplikasi yang ada, Anda harus memigrasikan
tema untuk menggunakan
MaterialTheme
untuk layar Compose apa pun. Ini berarti tema aplikasi Anda akan memiliki 2 sumber
kebenaran: tema berbasis View dan tema Compose. Setiap perubahan pada gaya visual
Anda harus dilakukan di beberapa tempat.
Jika rencana Anda adalah untuk memigrasikan aplikasi secara total ke Compose, Anda harus membuat versi Compose dari tema yang sudah ada. Masalahnya adalah, semakin awal Anda membuat tema Compose, semakin banyak pemeliharaan yang harus dilakukan selama pengembangan.
Adaptor Tema Compose MDC
Jika menggunakan library MDC di aplikasi Android, libraryAdaptor Tema Compose MDC memungkinkan Anda menggunakan kembali tema warna, tipografi, danbentuk dari tema berbasis View yang ada dalam komponen dengan mudah:
import com.google.android.material.composethemeadapter.MdcTheme
class ExampleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MdcTheme {
// Colors, typography, and shape have been read from the
// View-based theme used in this Activity
ExampleComposable(/*...*/)
}
}
}
}
Lihat dokumentasi library MDC untuk mengetahui informasi selengkapnya.
Adaptor Tema Compose AppCompat
Dengan Adaptor Tema Compose AppCompat, Anda dapat menggunakan kembali
tema XML AppCompat dengan mudah untuk tema
di Jetpack Compose. Ini akan membuat
MaterialTheme
dengan nilai warna dan
tipografi dari
tema konteks.
import com.google.accompanist.appcompattheme.AppCompatTheme
class ExampleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AppCompatTheme {
// Colors, typography, and shape have been read from the
// View-based theme used in this Activity
ExampleComposable(/*...*/)
}
}
}
}
Gaya komponen default
Baik library MDC maupun Adaptor Tema Compose AppCompat Compose tidak membaca gaya widget default yang ditentukan oleh tema. Ini karena Compose tidak memiliki konsep komponen default.
Baca gaya komponen dan sistem desain khusus selengkapnya di dokumentasi Tema.
Overlay Tema di Compose
Saat memigrasikan layar berbasis View ke Compose, perhatikan penggunaan
atribut android:theme
. Sepertinya Anda memerlukan
MaterialTheme
baru di bagian pohon UI Compose.
Baca selengkapnya di Panduan tema.
WindowInset dan Animasi IME
Anda dapat menangani WindowInsets
menggunakan
library accompanist-insets
yang menyediakan komponen dan pengubah untuk menanganinya dalam tata letak Anda, serta
dukungan untuk Animasi
IME.
class ExampleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
MaterialTheme {
ProvideWindowInsets {
MyScreen()
}
}
}
}
}
@Composable
fun MyScreen() {
Box {
FloatingActionButton(
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(16.dp) // normal 16dp of padding for FABs
.navigationBarsPadding(), // Move it out from under the nav bar
onClick = { }
) {
Icon( /* ... */)
}
}
}
Gambar 2. Animasi IME menggunakan library accompanist-insets.
Lihat dokumentasi library accompanist-insets untuk mengetahui informasi selengkapnya.
Menangani perubahan ukuran layar
Saat memigrasikan aplikasi yang menggunakan tata letak XML yang berbeda, bergantung pada ukuran layar,
gunakan komponen
BoxWithConstraints
untuk mengetahui ukuran minimum dan maksimum yang dapat mengisi.
@Composable
fun MyComposable() {
BoxWithConstraints {
if (minWidth < 480.dp) {
/* Show grid with 4 columns */
} else if (minWidth < 720.dp) {
/* Show grid with 8 columns */
} else {
/* Show grid with 12 columns */
}
}
}
Arsitektur dan sumber status kebenaran
Pola arsitektur Aliran Data Searah (UFD) berfungsi lancar dengan Compose. Jika aplikasi menggunakan jenis pola arsitektur lain, seperti Presenter View Model (MVP), sebaiknya migrasikan bagian UI tersebut ke UDF sebelum atau saat mengadopsi Compose.
ViewModel dalam Compose
Jika menggunakan library Architecture Components
ViewModel, Anda dapat mengakses
ViewModel
dari fungsi mana pun dengan
memanggil fungsi viewModel()
,
seperti yang dijelaskan dalam Dokumentasi integrasi Compose dengan
library umum.
Saat menggunakan Compose, berhati-hatilah saat menggunakan jenis ViewModel
yang sama dalam berbagai komponen karena elemen ViewModel
mengikuti cakupan siklus proses View. Cakupan
akan berupa aktivitas host, fragmen, atau grafik navigasi jika
library Navigasi digunakan.
Misalnya, jika komponen dapat dihosting dalam aktivitas, viewModel()
selalu
menampilkan instance yang sama yang hanya akan dihapus saat aktivitas telah selesai.
Pada contoh berikut, pengguna yang sama akan disapa dua kali karena instance
GreetingViewModel
yang sama digunakan kembali di semua komponen dalam aktivitas
host. Instance ViewModel
pertama yang telah dibuat digunakan kembali di komponen lain.
class ExampleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MaterialTheme {
Column {
Greeting("user1")
Greeting("user2")
}
}
}
}
}
@Composable
fun Greeting(userId: String) {
val greetingViewModel: GreetingViewModel = viewModel(
factory = GreetingViewModelFactory(userId)
)
val messageUser by greetingViewModel.message.observeAsState("")
Text(messageUser)
}
class GreetingViewModel(private val userId: String) : ViewModel() {
private val _message = MutableLiveData("Hi $userId")
val message: LiveData<String> = _message
}
Karena grafik navigasi juga mencakup elemen ViewModel
, komponen yang merupakan
tujuan dalam grafik navigasi memiliki instance ViewModel
yang berbeda.
Dalam hal ini, ViewModel
dimasukkan ke siklus proses tujuan dan
akan dihapus saat tujuan dihapus dari backstack. Pada contoh berikut,
saat pengguna membuka
layar Profil, instance baru GreetingViewModel
akan dibuat.
@Composable
fun MyScreen() {
NavHost(rememberNavController(), startDestination = "profile/{userId}") {
/* ... */
composable("profile/{userId}") { backStackEntry ->
Greeting(backStackEntry.arguments?.getString("userId") ?: "")
}
}
}
@Composable
fun Greeting(userId: String) {
val greetingViewModel: GreetingViewModel = viewModel(
factory = GreetingViewModelFactory(userId)
)
val messageUser by greetingViewModel.message.observeAsState("")
Text(messageUser)
}
Sumber status kebenaran
Saat Anda mengadopsi Compose di satu bagian UI, Compose dan kode sistem View mungkin perlu membagikan data. Jika memungkinkan, sebaiknya Anda merangkum status bersama tersebut di class lain yang mengikuti praktik terbaik UDF dan digunakan oleh kedua platform tersebut, misalnya, di ViewModel yang mengekspos aliran data dari data bersama untuk mengurangi update data.
Namun, hal ini tidak selalu mungkin jika data yang akan dibagikan dapat diubah atau terikat erat dengan elemen UI. Dalam hal ini, satu sistem harus menjadi sumber kebenaran, dan sistem tersebut perlu membagikan setiap update data ke sistem lain. Sebagai aturan umum, sumber kebenaran harus dimiliki oleh elemen mana pun yang lebih dekat dengan root hierarki UI.
Compose sebagai sumber kebenaran
Gunakan fungsi
SideEffect
untuk memublikasikan status Compose ke kode non-compose. Dalam hal ini,
sumber kebenaran disimpan dalam properti yang dapat disusun yang mengirim update status.
Misalnya,
OnBackPressedCallback
harus didaftarkan agar dapat memproses penekanan tombol kembali di
OnBackPressedDispatcher
.
Untuk memberitahukan apakah callback harus diaktifkan atau tidak, gunakan SideEffect
untuk mengupdate nilainya.
@Composable
fun BackHandler(
enabled: Boolean,
backDispatcher: OnBackPressedDispatcher,
onBack: () -> Unit
) {
// Safely update the current `onBack` lambda when a new one is provided
val currentOnBack by rememberUpdatedState(onBack)
// Remember in Composition a back callback that calls the `onBack` lambda
val backCallback = remember {
// Always intercept back events. See the SideEffect for a more complete version
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
currentOnBack()
}
}
}
// On every successful composition, update the callback with the `enabled` value
// to tell `backCallback` whether back events should be intercepted or not
SideEffect {
backCallback.isEnabled = enabled
}
// If `backDispatcher` changes, dispose and reset the effect
DisposableEffect(backDispatcher) {
// Add callback to the backDispatcher
backDispatcher.addCallback(backCallback)
// When the effect leaves the Composition, remove the callback
onDispose {
backCallback.remove()
}
}
}
Untuk mengetahui informasi efek samping selengkapnya, lihat Dokumentasi Siklus Proses dan Efek Samping.
Sistem View sebagai sumber kebenaran
Jika sistem View memiliki status dan membagikannya dengan Compose, sebaiknya
gabungkan status dalam objek mutableStateOf
agar Compose aman dari thread. Jika Anda menggunakan pendekatan ini, fungsi yang dapat dikomposisi akan disederhanakan karena
tidak lagi memiliki sumber kebenaran, tetapi sistem View harus mengupdate status
yang dapat diubah dan View yang menggunakan status tersebut.
Dalam contoh berikut, CustomViewGroup
berisi TextView
dan
ComposeView
dengan komponen TextField
di dalamnya. TextView
harus menampilkan
konten yang diketik pengguna di TextField
.
class CustomViewGroup @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0
) : LinearLayout(context, attrs, defStyle) {
// Source of truth in the View system as mutableStateOf
// to make it thread-safe for Compose
private var text by mutableStateOf("")
private val textView: TextView
init {
orientation = VERTICAL
textView = TextView(context)
val composeView = ComposeView(context).apply {
setContent {
MaterialTheme {
TextField(value = text, onValueChange = { updateState(it) })
}
}
}
addView(textView)
addView(composeView)
}
// Update both the source of truth and the TextView
private fun updateState(newValue: String) {
text = newValue
textView.text = newValue
}
}
Memigrasikan UI bersama
Jika bermigrasi secara bertahap ke Compose, Anda mungkin perlu menggunakan elemen UI bersama di sistem Compose dan View. Misalnya, jika aplikasi Anda memiliki
komponen CallToActionButton
khusus, Anda mungkin harus menggunakannya di layar Compose
dan juga layar berbasis View.
Di Compose, elemen UI bersama akan menjadi komponen dan dapat digunakan kembali di seluruh aplikasi,
apa pun elemen yang telah diberi gaya menggunakan XML atau menjadi tampilan khusus. Misalnya, Anda akan membuat komponen CallToActionButton
untuk komponen Button
pesan ajakan (CTA) khusus.
Untuk menggunakan komponen di layar berbasis View, Anda harus membuat
wrapper tampilan khusus yang diperluas dari AbstractComposeView
. Pada komponen Content
telah yang diganti, tempatkan fungsi yang sudah Anda buat dalam tema Compose
seperti yang ditunjukkan pada contoh di bawah:
@Composable
fun CallToActionButton(
text: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Button(
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.secondary
),
onClick = onClick,
modifier = modifier,
) {
Text(text)
}
}
class CallToActionViewButton @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0
) : AbstractComposeView(context, attrs, defStyle) {
var text by mutableStateOf<String>("")
var onClick by mutableStateOf<() -> Unit>({})
@Composable
override fun Content() {
YourAppTheme {
CallToActionButton(text, onClick)
}
}
}
Perhatikan bahwa parameter yang dapat disusun akan menjadi variabel yang dapat diubah di dalam tampilan
khusus. Ini akan membuat tampilan CallToActionViewButton
khusus menjadi inflatable dan dapat digunakan,
misalnya View Binding, seperti
tampilan tradisional. Lihat contoh di bawah:
class ExampleActivity : Activity() {
private lateinit var binding: ActivityExampleBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityExampleBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.callToAction.apply {
text = getString(R.string.something)
onClick = { /* Do something */ }
}
}
}
Jika komponen khusus berisi status yang dapat diubah, lihat bagian sumber status kebenaran di atas.
Pengujian
Anda dapat menguji gabungan kode View dan Compose secara bersamaan dengan menggunakan
API
createAndroidComposeRule()
. Untuk informasi selengkapnya, lihat Menguji tata letak Compose.
Studi Kasus: BroadcastReceivers
Untuk contoh fitur yang lebih realistis, Anda mungkin ingin bermigrasi atau menerapkannya
di Compose dan menampilkan CompositionLocal
dan efek samping, misalnya
BroadcastReceiver
harus didaftarkan dari
fungsi yang dapat dikomposisi.
Solusi ini memanfaatkan LocalContext
untuk menggunakan konteks saat ini, serta efek samping rememberUpdatedState
dan DisposableEffect
.
@Composable
fun SystemBroadcastReceiver(
systemAction: String,
onSystemEvent: (intent: Intent?) -> Unit
) {
// Grab the current context in this part of the UI tree
val context = LocalContext.current
// Safely use the latest onSystemEvent lambda passed to the function
val currentOnSystemEvent by rememberUpdatedState(onSystemEvent)
// If either context or systemAction changes, unregister and register again
DisposableEffect(context, systemAction) {
val intentFilter = IntentFilter(systemAction)
val broadcast = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
onSystemEvent(intent)
}
}
context.registerReceiver(broadcast, intentFilter)
// When the effect leaves the Composition, remove the callback
onDispose {
context.unregisterReceiver(broadcast)
}
}
}
@Composable
fun HomeScreen() {
SystemBroadcastReceiver(Intent.ACTION_BATTERY_CHANGED) { batteryStatus ->
val isCharging = /* Get from batteryStatus ... */ true
/* Do something if the device is charging */
}
/* Rest of the HomeScreen */
}