Ansichten in „Compose“ verwenden

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

Wenn Sie ein Ansichtselement oder eine Hierarchie einschließen möchten, verwenden Sie die zusammensetzbare Funktion AndroidView . AndroidView wird eine Lambda-Funktion übergeben, die ein 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 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 Bindung der Ansicht

Verwende zum Einbetten eines XML-Layouts 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 ein AndroidView in einer Lazy-Liste (LazyColumn, LazyRow, Pager usw.) verwenden, sollten Sie die AndroidView-Überlastung in Version 1.4.0-rc01 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. Der Wert darf nicht null sein, damit Ansichten wiederverwendet werden können.
  • onRelease (optional): Ein Callback, der aufgerufen wird, um zu signalisieren, dass der View die Zusammensetzung 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

Mit der zusammensetzbaren Funktion AndroidViewBinding können Sie in „Schreiben“ ein Fragment einfügen. AndroidViewBinding wird fragmentiert behandelt und entfernt z. B. das Fragment, wenn die zusammensetzbare Funktion die Zusammensetzung verlässt.

Dazu bläst du eine XML-Datei auf, die FragmentContainerView als Halter für Fragment enthält.

Wenn Sie beispielsweise my_fragment_layout.xml definiert haben, können Sie Code wie diesen verwenden und gleichzeitig 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" />

Blenden Sie dieses Fragment in Compose wie folgt auf:

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

Wenn Sie mehrere Fragmente im selben Layout verwenden müssen, achten Sie darauf, dass Sie für jede FragmentContainerView eine eindeutige ID definiert haben.

Android-Framework aus 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 zusammensetzbare Funktionen übergeben werden. Sie werden normalerweise mit einem Wert in einem bestimmten Knoten des UI-Baums bereitgestellt. Dieser Wert kann von seinen zusammensetzbaren Nachfolgerfunktionen verwendet werden, ohne CompositionLocal als Parameter in der zusammensetzbaren Funktion zu deklarieren.

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. Beachten Sie, dass den Klassen CompositionLocal Local vorangestellt wird, um die Auffindbarkeit durch die automatische Vervollständigung in der IDE zu verbessern.

Sie können über das Attribut current auf den aktuellen Wert einer CompositionLocal zugreifen. Der folgende Code zeigt beispielsweise eine Toast-Nachricht durch Angabe von LocalContext.current in der Methode Toast.makeToast.

@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: BroadcastReceivers.

Andere Interaktionen

Wenn für die gewünschte Interaktion kein Dienstprogramm definiert ist, empfiehlt es sich, der allgemeinen Richtlinie zum Schreiben zu folgen: Daten fließen nach unten, Ereignisse fließen nach oben (mehr dazu unter Thinking in Compose) 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 Funktionen, die Sie in Compose migrieren oder implementieren möchten, und die CompositionLocal und Nebeneffekte sehen Sie, wenn ein BroadcastReceiver-Objekt über eine zusammensetzbare 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 kennen jetzt die APIs zur Interoperabilität bei der Verwendung von „Schreiben“ in Views und umgekehrt. Auf der Seite Weitere Überlegungen finden Sie weitere Informationen.