Compose und andere Bibliotheken

Sie können Ihre bevorzugten Bibliotheken in Compose verwenden. In diesem Abschnitt wird beschrieben, wie Sie einige der nützlichsten Bibliotheken einbinden.

Aktivität

Wenn Sie Compose in einer Aktivität verwenden möchten, müssen Sie ComponentActivity verwenden, eine Unterklasse von Activity, die die entsprechenden LifecycleOwner- und -Komponenten für Compose bereitstellt. Außerdem bietet sie zusätzliche APIs, die Ihren Code von überschriebenen Methoden in Ihrer Aktivitätsklasse entkoppeln. Activity Compose stellt diese APIs für Composeables bereit, sodass das Überschreiben von Methoden außerhalb Ihrer Composeables oder das Abrufen einer expliziten Activity-Instanz nicht mehr erforderlich ist. Außerdem sorgen diese APIs dafür, dass sie nur einmal initialisiert werden, eine Neuzusammensetzung überstehen und ordnungsgemäß bereinigt werden, wenn das composable aus der Zusammensetzung entfernt wird.

Aktivitätsergebnis

Mit der rememberLauncherForActivityResult() API können Sie in Ihrem Composeable ein Ergebnis aus einer Aktivität abrufen:

@Composable
fun GetContentExample() {
    var imageUri by remember { mutableStateOf<Uri?>(null) }
    val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
        imageUri = uri
    }
    Column {
        Button(onClick = { launcher.launch("image/*") }) {
            Text(text = "Load Image")
        }
        Image(
            painter = rememberAsyncImagePainter(imageUri),
            contentDescription = "My Image"
        )
    }
}

Dieses Beispiel zeigt einen einfachen GetContent()-Vertrag. Durch Tippen auf die Schaltfläche wird die Anfrage gestartet. Das abschließende Lambda für rememberLauncherForActivityResult() wird aufgerufen, sobald der Nutzer ein Bild ausgewählt und zur Startaktivität zurückgekehrt ist. Dadurch wird das ausgewählte Bild mit der rememberImagePainter()-Funktion von Coil geladen.

Jede Unterklasse von ActivityResultContract kann als erstes Argument für rememberLauncherForActivityResult() verwendet werden. Sie können diese Technik also verwenden, um Inhalte aus dem Framework und in anderen gängigen Mustern anzufordern. Sie können auch eigene benutzerdefinierte Verträge erstellen und mit dieser Methode verwenden.

Laufzeitberechtigungen anfordern

Mit der Activity Result API und der oben beschriebenen rememberLauncherForActivityResult() können Sie Laufzeitberechtigungen anfordern. Verwenden Sie dazu den Vertrag RequestPermission für eine einzelne Berechtigung oder den Vertrag RequestMultiplePermissions für mehrere Berechtigungen.

Die Accompanist Permissions Library kann auch als Schicht über diesen APIs verwendet werden, um den aktuellen gewährten Status für Berechtigungen in einen Status abzubilden, der von Ihrer Compose-Benutzeroberfläche verwendet werden kann.

Zurück-Schaltfläche des Systems

Wenn Sie eine benutzerdefinierte Rücknavigation bereitstellen und das Standardverhalten der Systemschaltfläche „Zurück“ in Ihrem Composeable überschreiben möchten, können Sie mit einem BackHandler dieses Ereignis abfangen:

var backHandlingEnabled by remember { mutableStateOf(true) }
BackHandler(backHandlingEnabled) {
    // Handle back press
}

Mit dem ersten Argument wird festgelegt, ob BackHandler derzeit aktiviert ist. Mit diesem Argument können Sie den Handler je nach Status der Komponente vorübergehend deaktivieren. Das abschließende Lambda wird aufgerufen, wenn der Nutzer ein Systemrückgabeereignis auslöst und BackHandler derzeit aktiviert ist.

ViewModel

Wenn Sie die Architecture Components ViewModel-Bibliothek verwenden, können Sie über die Funktion viewModel() auf eine ViewModel aus jeder Composeable-Komponente zugreifen. Fügen Sie Ihrer Gradle-Datei die folgende Abhängigkeit hinzu:

Groovy

dependencies {
    implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.8.5'
}

Kotlin

dependencies {
    implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.5")
}

Anschließend können Sie die Funktion viewModel() in Ihrem Code verwenden.

class MyViewModel : ViewModel() { /*...*/ }

// import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun MyScreen(
    viewModel: MyViewModel = viewModel()
) {
    // use viewModel here
}

viewModel() gibt eine vorhandene ViewModel zurück oder erstellt eine neue. Standardmäßig ist der zurückgegebene ViewModel auf die umschließende Aktivität, das Fragment oder das Navigationsziel beschränkt und wird so lange beibehalten, wie der Bereich aktiv ist.

Wenn das Composeable beispielsweise in einer Aktivität verwendet wird, gibt viewModel() dieselbe Instanz zurück, bis die Aktivität beendet oder der Prozess beendet wird.

class MyViewModel : ViewModel() { /*...*/ }
// import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun MyScreen(
    // Returns the same instance as long as the activity is alive,
    // just as if you grabbed the instance from an Activity or Fragment
    viewModel: MyViewModel = viewModel()
) { /* ... */ }

@Composable
fun MyScreen2(
    viewModel: MyViewModel = viewModel() // Same instance as in MyScreen
) { /* ... */ }

Nutzungsrichtlinien

Normalerweise greifen Sie auf ViewModel-Instanzen auf Bildschirmebene zu, also in der Nähe eines Stamm-Composables, das von einer Aktivität, einem Fragment oder einem Ziel eines Navigationsdiagramms aufgerufen wird. Das liegt daran, dass ViewModel-Elemente standardmäßig auf diese Objekte auf Bildschirmebene beschränkt sind. Weitere Informationen zum Lebenszyklus und Umfang von ViewModel

Vermeiden Sie es, ViewModel-Instanzen an andere Composeables weiterzugeben, da dies die Tests dieser Composeables erschweren und Vorschauen beeinträchtigen kann. Übergeben Sie stattdessen nur die Daten und Funktionen, die sie benötigen, als Parameter.

Sie können ViewModel-Instanzen verwenden, um den Status für Composables auf Unterbildschirmebene zu verwalten. Beachten Sie jedoch den Lebenszyklus und den Umfang von ViewModel. Wenn das composable in sich geschlossen ist, können Sie Hilt verwenden, um die ViewModel einzuschleusen, damit Sie keine Abhängigkeiten von übergeordneten composables übergeben müssen.

Wenn Ihre ViewModel Abhängigkeiten hat, nimmt viewModel() einen optionalen Parameter ViewModelProvider.Factory an.

Weitere Informationen zu ViewModel in Compose und zur Verwendung von Instanzen mit der Navigation Compose-Bibliothek oder Aktivitäten und Fragmenten finden Sie in den Dokumenten zur Interoperabilität.

Datenstreams

Compose bietet Erweiterungen für die beliebtesten streambasierten Lösungen von Android. Jede dieser Erweiterungen wird von einem anderen Artefakt bereitgestellt:

Diese Artefakte registrieren sich als Listener und stellen die Werte als State dar. Jedes Mal, wenn ein neuer Wert ausgegeben wird, werden die Teile der Benutzeroberfläche, in denen diese state.value verwendet wird, neu zusammengesetzt. In diesem Code wird ShowData beispielsweise jedes Mal neu zusammengesetzt, wenn exampleLiveData einen neuen Wert ausgibt.

// import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun MyScreen(
    viewModel: MyViewModel = viewModel()
) {
    val dataExample = viewModel.exampleLiveData.observeAsState()

    // Because the state is read here,
    // MyScreen recomposes whenever dataExample changes.
    dataExample.value?.let {
        ShowData(dataExample)
    }
}

Asynchrone Vorgänge in Compose

Mit Jetpack Compose können Sie asynchrone Vorgänge mithilfe von Tasks aus Ihren Composables ausführen.

Weitere Informationen finden Sie in der Dokumentation zu Nebenwirkungen in den APIs LaunchedEffect, produceState und rememberCoroutineScope.

Die Navigationskomponente unterstützt Jetpack Compose-Anwendungen. Weitere Informationen finden Sie unter Mit Compose navigieren und Jetpack Navigation zu Navigation Compose migrieren.

Hilt

Hilt ist die empfohlene Lösung für die Abhängigkeitsinjektion in Android-Apps und funktioniert nahtlos mit Compose.

Die im Abschnitt ViewModel erwähnte viewModel()-Funktion verwendet automatisch das ViewModel, das Hilt mit der @HiltViewModel-Anmerkung erstellt. Wir haben eine Dokumentation mit Informationen zur ViewModel-Integration von Hilt bereitgestellt.

@HiltViewModel
class MyViewModel @Inject constructor(
    private val savedStateHandle: SavedStateHandle,
    private val repository: ExampleRepository
) : ViewModel() { /* ... */ }

// import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun MyScreen(
    viewModel: MyViewModel = viewModel()
) { /* ... */ }

Hilt und Navigation

Hilt lässt sich auch in die Navigation Compose-Bibliothek einbinden. Fügen Sie Ihrer Gradle-Datei die folgenden zusätzlichen Abhängigkeiten hinzu:

Groovy

dependencies {
    implementation 'androidx.hilt:hilt-navigation-compose:1.2.0'
}

Kotlin

dependencies {
    implementation("androidx.hilt:hilt-navigation-compose:1.2.0")
}

Verwenden Sie bei der Navigations-Funktion „Compose“ immer die hiltViewModel-Funktion, um eine Instanz Ihrer @HiltViewModel-annotierten ViewModel zu erhalten. Das funktioniert mit Fragmenten oder Aktivitäten, die mit @AndroidEntryPoint annotiert sind.

Wenn ExampleScreen beispielsweise ein Ziel in einem Navigationsgraphen ist, rufen Sie hiltViewModel() auf, um eine Instanz von ExampleViewModel abzurufen, die auf das Ziel beschränkt ist, wie im folgenden Code-Snippet gezeigt:

// import androidx.hilt.navigation.compose.hiltViewModel

@Composable
fun MyApp() {
    val navController = rememberNavController()
    val startRoute = "example"
    NavHost(navController, startDestination = startRoute) {
        composable("example") { backStackEntry ->
            // Creates a ViewModel from the current BackStackEntry
            // Available in the androidx.hilt:hilt-navigation-compose artifact
            val viewModel = hiltViewModel<MyViewModel>()
            MyScreen(viewModel)
        }
        /* ... */
    }
}

Wenn Sie die Instanz eines ViewModel mit dem Gültigkeitsbereich Navigationsrouten oder Navigationsgraph abrufen möchten, verwenden Sie stattdessen die zusammensetzbare Funktion hiltViewModel und übergeben Sie die entsprechende backStackEntry als Parameter:

// import androidx.hilt.navigation.compose.hiltViewModel
// import androidx.navigation.compose.getBackStackEntry

@Composable
fun MyApp() {
    val navController = rememberNavController()
    val startRoute = "example"
    val innerStartRoute = "exampleWithRoute"
    NavHost(navController, startDestination = startRoute) {
        navigation(startDestination = innerStartRoute, route = "Parent") {
            // ...
            composable("exampleWithRoute") { backStackEntry ->
                val parentEntry = remember(backStackEntry) {
                    navController.getBackStackEntry("Parent")
                }
                val parentViewModel = hiltViewModel<ParentViewModel>(parentEntry)
                ExampleWithRouteScreen(parentViewModel)
            }
        }
    }
}

Auslagerung

Mit der Paging-Bibliothek können Sie Daten nach und nach laden. Sie wird in Compose unterstützt. Die Seite zum Release der Seitenleiste enthält Informationen zur zusätzlichen paging-compose-Abhängigkeit, die dem Projekt und seiner Version hinzugefügt werden muss.

Hier ist ein Beispiel für die Compose APIs der Paging-Bibliothek:

@Composable
fun MyScreen(flow: Flow<PagingData<String>>) {
    val lazyPagingItems = flow.collectAsLazyPagingItems()
    LazyColumn {
        items(
            lazyPagingItems.itemCount,
            key = lazyPagingItems.itemKey { it }
        ) { index ->
            val item = lazyPagingItems[index]
            Text("Item is $item")
        }
    }
}

Weitere Informationen zur Verwendung der Paginierung in Compose finden Sie in der Dokumentation zu Listen und Rastern.

Maps

Mit der Maps Compose-Bibliothek können Sie Google Maps in Ihrer App verwenden. Hier ein Beispiel für die Verwendung:

@Composable
fun MapsExample() {
    val singapore = LatLng(1.35, 103.87)
    val cameraPositionState = rememberCameraPositionState {
        position = CameraPosition.fromLatLngZoom(singapore, 10f)
    }
    GoogleMap(
        modifier = Modifier.fillMaxSize(),
        cameraPositionState = cameraPositionState
    ) {
        Marker(
            state = remember { MarkerState(position = singapore) },
            title = "Singapore",
            snippet = "Marker in Singapore"
        )
    }
}