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 funkcji kompozycyjnej AndroidView . AndroidView otrzymuje wyrażenie lambda zwracające wartość View. Funkcja AndroidView udostępnia również wywołanie zwrotne update, które jest wywoływane, gdy widok danych jest zawyżony. AndroidView jest ponownie tworzony, gdy State odczytany w wywołaniu zwrotnym ulegnie zmianie. AndroidView, podobnie jak wiele innych wbudowanych funkcji kompozycyjnych, wykorzystuje parametr Modifier, którego można użyć np. do ustawienia pozycji w nadrzędnej funkcji kompozycyjnej.

@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 dostępnego w bibliotece androidx.compose.ui:ui-viewbinding. Aby to zrobić, musisz włączyć wiązanie widoku w projekcie.

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

AndroidView na listach leniwych

Jeśli używasz parametru AndroidView na liście leniwej (LazyColumn, LazyRow, Pager itp.), rozważ zastosowanie przeciążenia AndroidView wprowadzonego 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.

To przeciążenie tymi parametrami (AndroidView) dodaje 2 dodatkowe parametry:

  • onReset – wywołanie zwrotne wywoływane, aby zasygnalizować, że View ma zostać użyty ponownie. Aby można było ponownie użyć widoku danych, wartość nie może być pusta.
  • 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, upewnij się, że dla każdego z nich masz zdefiniowany 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 systemowych znajdziesz w artykule Zasoby w 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ładanej bez deklarowania CompositionLocal jako parametru w funkcji składanej.

Funkcja CompositionLocal służy do rozpowszechniania wartości dla typów platform Androida w tworzeniu, takich jak Context, Configuration lub View, w których kod tworzenia wiadomości jest hostowany z odpowiednim identyfikatorem LocalContext, LocalConfiguration lub LocalView. Pamiętaj, że klasy CompositionLocal są poprzedzone ciągiem Local, aby ułatwić ich wykrywanie przez funkcję autouzupełniania 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 za pomocą parametru 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: BroadcastReceivers na końcu tego dokumentu.

Inne interakcje

Jeśli nie ma określonego narzędzia odpowiadającego potrzebnej interakcji, sprawdzoną metodą jest przestrzeganie ogólnych wskazówek dotyczących tworzenia wiadomości: dane przepływają w dół, a zdarzenia zmieniają się w górę (szczegóły znajdziesz bardziej szczegółowo w artykule Thinking in Compose). Na przykład ten program kompozycyjny uruchamia inne działanie:

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 zostać zarejestrowana z funkcji składanej.

Rozwiązanie używa LocalContext do wykorzystania bieżącego kontekstu oraz efektów ubocznych 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

Znasz już interfejsy API interoperacyjności podczas korzystania z funkcji Utwórz w widokach danych i odwrotnie. Więcej informacji znajdziesz na stronie Inne uwagi.