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 stellt auch einen update-Callback bereit, der aufgerufen wird, wenn die Ansicht aufgebläht wird. Das AndroidView wird neu zusammengesetzt, wenn sich ein State-Lesevorgang innerhalb des Callbacks ändert. 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 in Ihrem Projekt die Ansichtsbindung 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. Aufgrund dieser Überlast kann Compose die zugrunde liegende View-Instanz wiederverwenden, wenn die enthaltene Komposition 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 View gleich verwendet 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

Mit der zusammensetzbaren Funktion AndroidViewBinding können Sie in „Schreiben“ eine Fragment hinzufü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.

Zusammensetzung von Ortsansässigen

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.

Mit CompositionLocal werden Werte für Android-Framework-Typen in Composer wie Context, Configuration oder die View weitergegeben, in denen der Compose-Code mit dem 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.

Sie können über das Attribut current auf den aktuellen Wert einer 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, der allgemeinen Compose-Richtlinie zu folgen: 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: Rundfunkempfä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 über eine kompostierbare Funktion registriert werden.

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

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.