Korzystanie z widoków w sekcji Utwórz

W interfejsie tworzenia możesz uwzględnić hierarchię widoku Androida. Ta metoda jest szczególnie przydatna, gdy chcesz skorzystać z elementów interfejsu, które nie są jeszcze dostępne w funkcji tworzenia wiadomości, np. AdView. Takie podejście umożliwia również ponowne wykorzystanie utworzonych wcześniej widoków niestandardowych.

Aby dołączyć element widoku danych lub hierarchię, użyj funkcji kompozycyjnej AndroidView . Funkcja AndroidView przekazuje lambda, która zwraca wartość View. AndroidView udostępnia też wywołanie zwrotne update, które jest wywoływane, gdy widok jest powiększony. AndroidView tworzy się ponownie za każdym razem, gdy zmieni się odczyt State w wywołaniu zwrotnym. AndroidView, podobnie jak wiele innych wbudowanych funkcji kompozycyjnych, przyjmuje parametr Modifier, którego można użyć na przykład do ustawienia pozycji w nadrzędnym elemencie kompozycyjnym.

@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 umieścić układ XML, użyj interfejsu API AndroidViewBinding udostępnianego przez bibliotekę androidx.compose.ui:ui-viewbinding. Aby to zrobić, musisz włączyć w projekcie powiązanie widoku danych.

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

AndroidView na leniwszych listach

Jeśli używasz elementu AndroidView na liście Leniwo (LazyColumn, LazyRow, Pager itp.), rozważ zastosowanie przeciążenia AndroidView wprowadzonego w wersji 1.4.0-rc01. To przeciążenie umożliwia ponowne wykorzystanie bazowej instancji View w przypadku ponownego użycia kompozycji, w której występuje kompozycja (tak jak w przypadku leniwych list).

Przeciążenie obiektu AndroidView powoduje dodanie 2 dodatkowych parametrów:

  • onReset – wywołanie zwrotne w celu zasygnalizowania, że View zostanie ponownie użyty. To pole nie może mieć wartości null, aby umożliwić ponowne używanie widoków.
  • onRelease (opcjonalnie) – wywołanie zwrotne w celu zasygnalizowania, że komponent View zakończył kompozycję i nie jest ponownie używany.

@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 oknie tworzenia

Użyj funkcji AndroidViewBinding kompozycyjnej, aby dodać element Fragment w sekcji Utwórz. Funkcja AndroidViewBinding obsługuje fragmenty z obsługą fragmentów, na przykład usuwa fragment, gdy funkcja kompozycyjna opuści kompozycję.

Aby to zrobić, dodaj kod XML zawierający FragmentContainerView jako właściciela Twojego elementu Fragment.

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

Rozszerz ten fragment w oknie Utwórz w następujący sposób:

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

Jeśli musisz użyć wielu fragmentów w tym samym układzie, upewnij się, że każdy element FragmentContainerView ma unikalny identyfikator.

Wywoływanie platformy Android z poziomu Compose

Tworzenie działa w ramach klas platformy Androida. Na przykład jest hostowane w klasach Android View, takich jak Activity czy Fragment, i może korzystać z klas platformy Androida, takich jak Context, zasoby systemowe, Service czy BroadcastReceiver.

Więcej informacji o zasobach systemowych znajdziesz w artykule Zasoby w oknie tworzenia wiadomości.

Lokalna kompozycja

Klasy CompositionLocal umożliwiają pośrednie przekazywanie danych za pomocą funkcji kompozycyjnych. Zwykle mają one wartość w określonym węźle drzewa interfejsu. Jej elementy podrzędne mogą być używane przez kompozycyjne elementy podrzędne bez deklarowania CompositionLocal jako parametru funkcji kompozycyjnej.

CompositionLocal służy do propagowania wartości typów platform Androida w sekcji Utwórz, takich jak Context, Configuration czy View, gdzie kod tworzenia wiadomości jest hostowany z odpowiednimi parametrami LocalContext, LocalConfiguration i LocalView. Pamiętaj, że klasy CompositionLocal mają prefiks Local, co ułatwia wykrywanie dzięki autouzupełnianiu w IDE.

Aby uzyskać dostęp do bieżącej wartości elementu CompositionLocal, użyj jego właściwości current. Na przykład poniższy kod wyświetla toast, podając parametr LocalContext.current w metodzie Toast.makeToast.

@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: BroadcastReceivedrs na końcu tego dokumentu.

Inne interakcje

Jeśli nie ma zdefiniowanego narzędzia do danej interakcji, sprawdzoną metodą jest stosowanie się do ogólnych wytycznych dotyczących tworzenia wiadomości, przepływu danych w dół, w górę zdarzeń (więcej szczegółów na ten temat znajdziesz w sekcji Myślenie tworzenia wiadomości). Na przykład ten element kompozycyjny 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

Aby uzyskać bardziej realistyczny przykład funkcji, które warto przenieść lub zaimplementować w tworzeniu, oraz zaprezentować CompositionLocal i efekty uboczne, załóżmy, że BroadcastReceiver trzeba zarejestrować za pomocą funkcji kompozycyjnej.

Rozwiązanie wykorzystuje uprawnienia LocalContext do wykorzystania bieżącego kontekstu oraz efekty uboczne rememberUpdatedState i 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 */
}

Dalsze kroki

Teraz, gdy znasz już interfejsy API interoperacyjności podczas korzystania z tworzenia w widokach i odwrotnie, zapoznaj się ze stroną Inne uwagi, aby dowiedzieć się więcej.