Menggunakan 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. Pendekatan ini juga memungkinkan Anda menggunakan kembali tampilan kustom yang mungkin sudah Anda desain.

Untuk menyertakan elemen tampilan atau hierarki, gunakan composable AndroidView . AndroidView dimasukkan 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. AndroidView, seperti banyak composable bawaan lainnya, memerlukan parameter Modifier yang dapat digunakan, misalnya, untuk menyetel posisinya di composable induk.

@Composable
fun CustomView() {
    var selectedItem by remember { mutableStateOf(0) }

    // Adds view to Compose
    AndroidView(
        modifier = Modifier.fillMaxSize(), // Occupy the max size in the Compose UI tree
        factory = { context ->
            // Creates view
            MyView(context).apply {
                // Sets up listeners for View -> Compose communication
                setOnClickListener {
                    selectedItem = 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.selectedItem = selectedItem
        }
    )
}

@Composable
fun ContentExample() {
    Column(Modifier.fillMaxSize()) {
        Text("Look at this CustomView!")
        CustomView()
    }
}

AndroidView dengan view binding

Untuk menyematkan tata letak XML, gunakan AndroidViewBinding API, yang disediakan oleh library androidx.compose.ui:ui-viewbinding. Untuk melakukannya, project Anda harus mengaktifkan binding tampilan.

@Composable
fun AndroidViewBindingExample() {
    AndroidViewBinding(ExampleLayoutBinding::inflate) {
        exampleView.setBackgroundColor(Color.GRAY)
    }
}

AndroidView dalam daftar Lambat

Jika Anda menggunakan AndroidView dalam daftar Lambat (LazyColumn, LazyRow, Pager, dll.), pertimbangkan untuk menggunakan overload AndroidView yang diperkenalkan di versi 1.4.0-rc01. Overload ini memungkinkan Compose menggunakan kembali instance View yang mendasarinya saat komposisi yang memuatnya digunakan kembali seperti yang terjadi untuk daftar Lambat.

Overload AndroidView ini menambahkan 2 parameter tambahan:

  • onReset - Callback yang dipanggil untuk menandakan bahwa View akan digunakan kembali. Nilai ini tidak boleh null untuk mengaktifkan penggunaan ulang Tampilan.
  • onRelease (opsional) - Callback yang dipanggil untuk menandakan bahwa View telah keluar dari komposisi dan tidak akan digunakan kembali lagi.

@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun AndroidViewInLazyList() {
    LazyColumn {
        items(100) { index ->
            AndroidView(
                modifier = Modifier.fillMaxSize(), // Occupy the max size in the Compose UI tree
                factory = { context ->
                    MyView(context)
                },
                update = { view ->
                    view.selectedItem = index
                },
                onReset = { view ->
                    view.clear()
                }
            )
        }
    }
}

Fragment di Compose

Gunakan composable AndroidViewBinding untuk menambahkan Fragment di Compose. AndroidViewBinding memiliki penanganan khusus fragmen seperti menghapus fragmen saat composable keluar dari komposisi.

Lakukan dengan meng-inflate XML yang berisi FragmentContainerView sebagai holder untuk Fragment.

Misalnya, jika memiliki my_fragment_layout.xml yang ditentukan, Anda dapat menggunakan kode seperti ini saat mengganti atribut XML android:name dengan nama class Fragment:

<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragment_container_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:name="com.example.compose.snippets.interop.MyFragment" />

Inflate fragmen ini di Compose sebagai berikut:

@Composable
fun FragmentInComposeExample() {
    AndroidViewBinding(MyFragmentLayoutBinding::inflate) {
        val myFragment = fragmentContainerView.getFragment<MyFragment>()
        // ...
    }
}

Jika Anda perlu menggunakan beberapa fragmen dalam tata letak yang sama, pastikan Anda telah menentukan ID unik untuk setiap FragmentContainerView.

Memanggil framework Android dari Compose

Compose beroperasi dalam class framework Android. Misalnya, class tersebut dihosting di class View Android, seperti Activity atau Fragment, dan mungkin menggunakan class framework Android seperti Context, resource sistem, Service, atau BroadcastReceiver.

Untuk mempelajari resource sistem lebih lanjut, lihat Resource dalam Compose.

Lokal Komposisi

Dengan class CompositionLocal, penerusan data secara implisit bisa dilakukan melalui fungsi composable. Fungsi ini biasanya diberi nilai dalam node tertentu dari pohon UI. Nilai tersebut dapat digunakan oleh turunan composable-nya tanpa mendeklarasikan CompositionLocal sebagai parameter dalam fungsi composable.

CompositionLocal digunakan untuk mengisi nilai jenis framework Android di Compose seperti Context, Configuration, atau View 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 ini menampilkan pesan toast dengan menyediakan LocalContext.current ke dalam metode Toast.makeToast.

@Composable
fun ToastGreetingButton(greeting: String) {
    val context = LocalContext.current
    Button(onClick = {
        Toast.makeText(context, greeting, Toast.LENGTH_SHORT).show()
    }) {
        Text("Greet")
    }
}

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 Paradigma Compose). Misalnya, composable ini akan meluncurkan aktivitas yang berbeda:

class OtherInteractionsActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // get data from savedInstanceState
        setContent {
            MaterialTheme {
                ExampleComposable(data, onButtonClick = {
                    startActivity(Intent(this, MyActivity::class.java))
                })
            }
        }
    }
}

@Composable
fun ExampleComposable(data: DataExample, onButtonClick: () -> Unit) {
    Button(onClick = onButtonClick) {
        Text(data.title)
    }
}

Studi Kasus: Penerima siaran

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 composable.

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?) {
                currentOnSystemEvent(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 */
}

Langkah berikutnya

Setelah Anda mengetahui API interoperabilitas saat menggunakan Compose di View dan sebaliknya, jelajahi halaman Pertimbangan lainnya untuk mempelajari lebih lanjut.