Korzystanie z widoków w sekcji Utwórz

W interfejsie tworzenia możesz uwzględnić hierarchię widoku Androida. To podejście jest szczególnie przydatne, jeśli chcesz używać elementów interfejsu użytkownika, które nie są jeszcze dostępne w Compose, takich jak AdView. Dzięki temu możesz też ponownie używać utworzonych wcześniej widoków niestandardowych.

Aby uwzględnić element widoku lub hierarchię, użyj kompozytu AndroidView . AndroidView otrzymuje wyrażenie lambda zwracające wartość View. AndroidView udostępnia też funkcję update, która jest wywoływana, gdy widok jest napełniany. AndroidView jest ponownie tworzony, gdy State odczytany w wywołaniu zwrotnym ulegnie zmianie. AndroidView, podobnie jak wiele innych wbudowanych komponentów, przyjmuje parametr Modifier, który można wykorzystać na przykład do ustawienia jego pozycji w komponencie nadrzędnym.

@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 z powiązaniem widoku

Aby osadzić układ XML, użyj interfejsu AndroidViewBinding, który jest udostępniany przez bibliotekę androidx.compose.ui:ui-viewbinding. Aby to zrobić, musisz włączyć w projekcie wiązanie widoku.

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

AndroidView na listach leniwych

Jeśli używasz elementu AndroidView na liście Lazy (LazyColumn, LazyRow, Pager itp.), rozważ użycie przeciążenia AndroidView, które zostało wprowadzone w wersji 1.4.0-rc01. Ta przeciążenie pozwala Compose używać ponownie instancji View, gdy kompozycja zawierająca jest używana ponownie w takim samym stanie, jak w przypadku list leniwych.

Ta przeciążona wersja funkcji AndroidView zawiera 2 dodatkowe parametry:

  • onReset – wywołanie zwrotne wywoływane, aby zasygnalizować, że View ma zostać użyty ponownie. Aby umożliwić ponowne użycie widoku, wartość tego pola nie może być równa null.
  • onRelease (opcjonalnie) – wywołanie zwrotne wywoływane, aby zasygnalizować, że View opuścił kompozycję i nie będzie już używany ponownie.

@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()
                }
            )
        }
    }
}

Fragmenty w edytorze

Aby dodać Fragment w sekcji Redagowanie, użyj komponentu AndroidViewBinding. AndroidViewBinding obsługuje fragmenty na różne sposoby, np. usuwa fragment, gdy kompozybilny element opuszcza kompozycję.

Aby to zrobić, wczytaj plik XML zawierający element FragmentContainerView, który będzie stanowił element Fragment.

Jeśli na przykład masz zdefiniowany element my_fragment_layout.xml, możesz użyć takiego kodu, zastępując atrybut XML android:name nazwą klasy elementu 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" />

W edytorze tekstu rozwiń ten fragment w ten sposób:

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

Jeśli w tym samym układzie musisz użyć kilku fragmentów, pamiętaj, aby dla każdego z nich zdefiniować unikalny identyfikator.FragmentContainerView

Wywoływanie platformy Android z Compose

Compose działa w ramach klas frameworka Androida. Na przykład jest ona hostowana na klasach widoku Androida, takich jak Activity lub Fragment, i może używać klas frameworka Androida, takich jak Context, zasoby systemowe, Service lub BroadcastReceiver.

Więcej informacji o zasobach systemu znajdziesz w artykule Zasoby w usłudze Compose.

Kompozycja Lokalna

CompositionLocal klasy umożliwiają przekazywanie danych w drugim planie za pomocą funkcji składanych. Zwykle są one dostarczane z wartością w określonym węźle drzewa interfejsu. Ta wartość może być używana przez potomków funkcji składanych bez deklarowania CompositionLocal jako parametru w funkcji składanej.

CompositionLocal służy do propagowania wartości typów platformy Android w Compose, takich jak Context, Configuration lub View, w których hostowany jest kod Compose z odpowiednim LocalContext, LocalConfiguration lub LocalView. Pamiętaj, że klasy CompositionLocal mają prefiks Local, aby ułatwić ich znajdowanie za pomocą autouzupełniania w IDE.

Uzyskaj dostęp do bieżącej wartości elementu CompositionLocal, korzystając z właściwości current. Na przykład kod poniżej wyświetla komunikat toast, przekazując do metody Toast.makeToast wartość LocalContext.current.

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

Pełniejszy przykład znajdziesz w sekcji Studium przypadku: BroadcastReceivers na końcu tego dokumentu.

Inne interakcje

Jeśli nie ma narzędzia do interakcji, których potrzebujesz, najlepiej postępować zgodnie z ogólnymi wytycznymi dotyczącymi tworzenia wiadomości: dane przepływają w dół, zdarzenia w górę (więcej informacji znajdziesz w artykule Myślenie o komponowaniu wiadomości). Na przykład ta kompozycja uruchamia inną aktywność:

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)
    }
}

Studium przypadku: odbiorniki sygnału radiowego

Aby pokazać bardziej realistyczny przykład funkcji, które warto przenieść lub zaimplementować w Compose, oraz zaprezentować CompositionLocalefekty uboczne, załóżmy, że BroadcastReceiver musi być zarejestrowana z funkcji składanej.

Rozwiązanie korzysta z elementu LocalContext, aby wykorzystać bieżący kontekst, oraz z elementów rememberUpdatedStateDisposableEffect, aby uwzględnić skutki uboczne.

@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 */
}

Dalsze kroki

Teraz, gdy znasz interfejsy API interoperacyjności podczas korzystania z Compose in Views i odwrotnie, zapoznaj się ze stroną Inne kwestie, aby dowiedzieć się więcej.