Ansichten in „Compose“ verwenden

Sie können eine Android-View-Hierarchie in eine Benutzeroberfläche zum Schreiben einfügen. Dieser Ansatz ist besonders nützlich, wenn Sie UI-Elemente verwenden möchten, die in Composer noch nicht verfügbar sind, z. B. AdView. Auf diese Weise können Sie auch benutzerdefinierte Ansichten wiederverwenden, die Sie erstellt haben.

Verwenden Sie die zusammensetzbare Funktion AndroidView , um ein Ansichtselement oder eine Hierarchie einzufügen. AndroidView wird ein Lambda übergeben, das ein View zurückgibt. AndroidView bietet auch einen update-Callback, der aufgerufen wird, wenn die Ansicht aufgebläht ist. Das AndroidView wird neu zusammengesetzt, wenn sich ein State-Lesevorgang im Callback ändert. AndroidView verwendet wie viele andere integrierte zusammensetzbare Funktionen einen Modifier-Parameter, mit dem beispielsweise seine Position in der übergeordneten zusammensetzbaren Funktion festgelegt werden kann.

@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

Verwenden Sie zum Einbetten eines XML-Layouts die AndroidViewBinding API, die von der androidx.compose.ui:ui-viewbinding-Bibliothek bereitgestellt wird. Dazu muss für Ihr Projekt die Ansichtsbindung aktiviert sein.

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

AndroidView in Lazy-Listen

Wenn Sie ein AndroidView in einer Lazy List (LazyColumn, LazyRow, Pager usw.) verwenden, sollten Sie die in Version 1.4.0-rc01 eingeführte AndroidView-Überlastung in Betracht ziehen. Durch diese Überlastung kann Compose die zugrunde liegende View-Instanz wiederverwenden, wenn die enthaltende Zusammensetzung wiederverwendet wird, wie es bei Lazy-Listen der Fall ist.

Diese Überlastung von AndroidView fügt zwei zusätzliche Parameter hinzu:

  • onReset: Ein Callback, der aufgerufen wird, um zu signalisieren, dass der View bald verwendet wird. Dieser darf nicht null sein, um die Wiederverwendung von Ansichten zu aktivieren.
  • onRelease (optional): Ein Callback, der aufgerufen wird, um zu signalisieren, dass View die Komposition verlassen hat und nicht noch einmal verwendet 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 die zusammensetzbare Funktion AndroidViewBinding, um dem Tool „Schreiben“ eine Fragment hinzuzufügen. AndroidViewBinding hat eine fragmentspezifische Verarbeitung, wie das Entfernen des Fragments, wenn die zusammensetzbare Funktion die Komposition verlässt.

Dazu schlüpfst du in die XML-Datei, die einen FragmentContainerView als Halter für deine Fragment enthält.

Wenn du beispielsweise my_fragment_layout.xml definiert hast, kannst du Code wie diesen verwenden und dabei das XML-Attribut android:name durch den Klassennamen deiner 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" />

Infließen Sie dieses Fragment in der Funktion „Compose“ wie folgt:

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

Wenn du mehrere Fragmente im selben Layout verwenden musst, musst du für jede FragmentContainerView eine eindeutige ID definiert haben.

Android-Framework aus Compose aufrufen

Compose wird innerhalb der 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.

Zusammensetzung von lokalen Nutzern

CompositionLocal-Klassen ermöglichen die implizite Weitergabe von Daten über zusammensetzbare Funktionen. Sie sind normalerweise mit einem Wert in einem bestimmten Knoten des UI-Baums versehen. Dieser Wert kann von seinen zusammensetzbaren Nachfolgerelementen verwendet werden, ohne dass CompositionLocal als Parameter in der zusammensetzbaren Funktion deklariert wird.

Mit CompositionLocal werden Werte für Android-Framework-Typen in Composer weitergegeben, z. B. Context, Configuration oder View, in denen der Code zum Schreiben mit dem entsprechenden LocalContext, LocalConfiguration oder LocalView gehostet wird. Beachten Sie, dass CompositionLocal-Klassen das Präfix Local vorangestellt ist, um die Auffindbarkeit mit der automatischen Vervollständigung in der IDE zu verbessern.

Sie können mithilfe des Attributs current auf den aktuellen Wert einer CompositionLocal zugreifen. Der folgende Code zeigt beispielsweise eine Toast-Nachricht, indem LocalContext.current in der Methode Toast.makeToast bereitgestellt wird.

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

Ein ausführlicheres Beispiel findest du im Abschnitt Fallstudie: BroadcastReceivers am Ende dieses Dokuments.

Andere Interaktionen

Wenn für die von Ihnen benötigte Interaktion kein Dienstprogramm definiert ist, sollten Sie der allgemeinen Richtlinie zum Schreiben folgen: Daten fließen nach unten, Ereignisse steigen (ausführlicher wird dies unter Das Denken in die Erstellung von Inhalten ausführlicher erläutert). Diese zusammensetzbare Funktion startet beispielsweise eine andere Aktivität:

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: Rundfunkempfänger

Ein realistischeres Beispiel für Features, die Sie in Compose migrieren oder implementieren möchten, und um CompositionLocal und Nebeneffekte darzustellen, nehmen wir an, dass BroadcastReceiver aus einer zusammensetzbaren Funktion registriert werden muss.

Die Lösung nutzt LocalContext, 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

Sie sind nun mit den Interoperabilitäts-APIs beim Verwenden der Funktion „Schreiben“ in Ansichten vertraut und umgekehrt. Weitere Informationen finden Sie auf der Seite Weitere Überlegungen.