Ansichten in „Compose“ verwenden

Sie können eine Android-Ansichtshierarchie in eine Compose-Benutzeroberfläche einbinden. Dieser Ansatz ist besonders nützlich, wenn Sie UI-Elemente verwenden möchten, die in Compose noch nicht verfügbar sind, z. B. AdView. Außerdem können Sie so benutzerdefinierte Ansichten wiederverwenden, die Sie möglicherweise erstellt haben.

Wenn Sie ein Ansichtselement oder eine Hierarchie einfügen möchten, verwenden Sie das AndroidView -Element „composable“. AndroidView wird ein Lambda übergeben, das eine View zurückgibt. AndroidView bietet auch einen update-Callback, der aufgerufen wird, wenn die Ansicht maximiert wird. Die AndroidView wird neu erstellt, wenn sich eine State ändert, die im Rückruf gelesen wird. AndroidView nimmt wie viele andere integrierte Elemente den Parameter Modifier an, mit dem sich beispielsweise die Position im übergeordneten Element festlegen lässt.

@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 mit Ansichtsbindung

Wenn du ein XML-Layout einbetten möchtest, verwende die AndroidViewBinding API, die von der androidx.compose.ui:ui-viewbinding-Bibliothek bereitgestellt wird. Dazu muss die Ansichtsbindung in Ihrem Projekt aktiviert sein.

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

AndroidView in Lazy-Listen

Wenn Sie AndroidView in einer Lazy-Liste (LazyColumn, LazyRow, Pager usw.) verwenden, sollten Sie die in Version 1.4.0-rc01 eingeführte Überladung von AndroidView verwenden. Mit dieser Überladung kann Compose die zugrunde liegende View-Instanz wiederverwenden, wenn die enthaltene Komposition wiederverwendet wird, wie es bei Lazy-Listen der Fall ist.

Bei dieser Überladung von AndroidView werden zwei zusätzliche Parameter hinzugefügt:

  • onReset: Ein Callback, der aufgerufen wird, um anzuzeigen, dass der View wiederverwendet werden soll. Dieser Wert darf nicht null sein, damit die Wiederverwendung der Ansicht möglich ist.
  • onRelease (optional): Ein Callback, der aufgerufen wird, um anzuzeigen, dass die View die Komposition verlassen hat und nicht wiederverwendet wird.

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

Fragmente in Compose

Verwenden Sie das AndroidViewBinding-Element, um in Compose ein Fragment hinzuzufügen. AndroidViewBinding hat fragmentspezifische Funktionen, z. B. das Entfernen des Fragments, wenn das Composed-Element die Komposition verlässt.

Dazu müssen Sie eine XML-Datei mit einer FragmentContainerView als Inhaber Ihrer Fragment aufblähen.

Wenn Sie beispielsweise my_fragment_layout.xml definiert haben, können Sie Code wie diesen verwenden und dabei das XML-Attribut android:name durch den Klassennamen Ihrer Fragment ersetzen:

<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" />

Maximieren Sie dieses Fragment in „Compose“ so:

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

Wenn Sie mehrere Fragmente im selben Layout verwenden möchten, müssen Sie für jede FragmentContainerView eine eindeutige ID definieren.

Android-Framework über Compose aufrufen

Compose wird in den Android-Framework-Klassen ausgeführt. Sie wird beispielsweise in Android View-Klassen wie Activity oder Fragment gehostet und kann Android-Framework-Klassen wie Context, Systemressourcen, Service oder BroadcastReceiver verwenden.

Weitere Informationen zu Systemressourcen finden Sie unter Ressourcen in Compose.

Komposition – Lokale

Mit CompositionLocal-Klassen können Daten implizit über kombinierbare Funktionen übergeben werden. Sie werden in der Regel in einem bestimmten Knoten des UI-Baums mit einem Wert versehen. Dieser Wert kann von den untergeordneten Elementen verwendet werden, ohne dass CompositionLocal als Parameter in der zusammensetzbaren Funktion deklariert werden muss.

CompositionLocal wird verwendet, um Werte für Android-Frameworktypen in Compose zu übertragen, z. B. Context, Configuration oder View, in dem der Compose-Code mit der entsprechenden LocalContext, LocalConfiguration oder LocalView gehostet wird. CompositionLocal-Klassen haben das Präfix Local, damit sie bei der automatischen Vervollständigung in der IDE leichter gefunden werden.

Über die Property current können Sie auf den aktuellen Wert eines CompositionLocal zugreifen. Im folgenden Code wird beispielsweise eine Toast-Nachricht angezeigt, indem LocalContext.current in die Methode Toast.makeToast übergeben wird.

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

Ein vollständigeres Beispiel finden Sie am Ende dieses Dokuments im Abschnitt Fallstudie: BroadcastReceiver.

Sonstige Interaktionen

Wenn für die gewünschte Interaktion kein Dienstprogramm definiert ist, empfiehlt es sich, die allgemeine Compose-Richtlinie zu befolgen: Daten fließen nach unten, Ereignisse nach oben. Weitere Informationen finden Sie unter In Compose denken. Mit diesem Composeable wird beispielsweise eine andere Aktivität gestartet:

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

Fallstudie: Broadcast-Empfänger

Ein realistischeres Beispiel für Funktionen, die Sie in Compose migrieren oder implementieren möchten, und um CompositionLocal und Nebeneffekte zu veranschaulichen: Angenommen, eine BroadcastReceiver muss von einer zusammensetzbaren Funktion registriert werden.

In der Lösung wird LocalContext verwendet, um den aktuellen Kontext zu verwenden, sowie die Nebenwirkungen rememberUpdatedState und 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 */
}

Nächste Schritte

Nachdem Sie nun die APIs zur Interoperabilität kennen, die bei der Verwendung von Compose in Ansichten und umgekehrt verwendet werden, können Sie auf der Seite Weitere Überlegungen weitere Informationen finden.