Tworzenie i inne biblioteki

W Compose możesz używać ulubionych bibliotek. W tej sekcji opisano, jak włączyć kilka najbardziej przydatnych bibliotek.

Aktywność

Aby używać Compose w aktywności, musisz użyć ComponentActivity, podklasy Activity, która udostępnia odpowiednie LifecycleOwner i komponenty dla Compose. Zawiera też dodatkowe interfejsy API, które oddzielają kod od zastępowania metod w klasie aktywności. Activity Compose udostępnia te interfejsy API komponentom, dzięki czemu nie trzeba już zastępować metod poza komponentami ani pobierać jawnej instancji Activity. Ponadto te interfejsy API zapewniają, że są inicjowane tylko raz, przetrwają ponowne komponowanie i są prawidłowo czyszczone, jeśli funkcja kompozycyjna zostanie usunięta z kompozycji.

Wynik aktywności

Interfejs API rememberLauncherForActivityResult() umożliwia uzyskanie wyniku działania w funkcji kompozycyjnej:

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

Ten przykład przedstawia prostą umowę GetContent(). Kliknięcie przycisku powoduje wysłanie prośby. Funkcja lambda końcowa dla rememberLauncherForActivityResult() jest wywoływana, gdy użytkownik wybierze obraz i wróci do aktywności uruchamiającej. Spowoduje to wczytanie wybranego obrazu za pomocą funkcji rememberImagePainter()biblioteki Coil.

Jako pierwszy argument funkcji rememberLauncherForActivityResult() można użyć dowolnej podklasy ActivityResultContract. Oznacza to, że możesz używać tej techniki do wysyłania do platformy żądań dotyczących treści i innych typowych wzorców. Możesz też tworzyć własne umowy niestandardowe i używać ich w tej technice.

Wysyłanie prośby o uprawnienia w czasie działania

Ten sam interfejs Activity Result API i rememberLauncherForActivityResult(), o którym pisaliśmy powyżej, może służyć do wysyłania próśb o uprawnienia w czasie działania za pomocą kontraktu RequestPermission dla pojedynczego uprawnienia lub kontraktu RequestMultiplePermissions dla wielu uprawnień.

Biblioteka uprawnień towarzyszących może być też używana jako warstwa nad tymi interfejsami API do mapowania bieżącego stanu przyznanych uprawnień na stan, którego może używać interfejs Compose.

Obsługa systemowego przycisku Wstecz

Aby zapewnić niestandardową nawigację wstecz i zastąpić domyślne działanie systemowego przycisku Wstecz w kompozycji, możesz użyć BackHandler, aby przechwycić to zdarzenie:

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

Pierwszy argument określa, czy element BackHandler jest obecnie włączony. Możesz go użyć, aby tymczasowo wyłączyć moduł obsługi na podstawie stanu komponentu. Funkcja lambda na końcu zostanie wywołana, jeśli użytkownik wywoła zdarzenie systemowe „wstecz”, a BackHandler jest obecnie włączone.

ViewModel

Jeśli używasz biblioteki Architecture Components ViewModel, możesz uzyskać dostęp do ViewModel z dowolnego komponentu kompozycyjnego, wywołując funkcję viewModel(). Dodaj do pliku Gradle tę zależność:

Groovy

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

Kotlin

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

Następnie możesz użyć w kodzie funkcji viewModel().

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

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

viewModel() zwraca istniejący ViewModel lub tworzy nowy. Domyślnie zwrócony obiekt ViewModel jest ograniczony do otaczającego działania, fragmentu lub miejsca docelowego nawigacji i jest przechowywany tak długo, jak długo jest aktywny zakres.

Jeśli np. funkcja kompozycyjna jest używana w aktywności, viewModel() zwraca tę samą instancję do momentu zakończenia aktywności lub zakończenia procesu.

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
) { /* ... */ }

Wytyczne dotyczące użytkowania

Zwykle uzyskujesz dostęp do instancji ViewModel w komponentach na poziomie ekranu, czyli w pobliżu komponentu głównego wywoływanego z aktywności, fragmentu lub miejsca docelowego wykresu nawigacji. Dzieje się tak, ponieważ ViewModels są domyślnie ograniczone do obiektów na poziomie ekranu. Więcej informacji o cyklu życia i zakresie ViewModelznajdziesz tutaj.

Staraj się nie przekazywać instancji ViewModel do innych funkcji kompozycyjnych, ponieważ może to utrudnić testowanie tych funkcji i spowodować nieprawidłowe działanie podglądów. Zamiast tego przekazuj tylko dane i funkcje, których potrzebują, jako parametry.

Możesz używać instancji ViewModel do zarządzania stanem w przypadku komponentów kompozycyjnych na poziomie podrzędnego ekranu, ale pamiętaj o cyklu życia i zakresie ViewModel. Jeśli funkcja kompozycyjna jest samodzielna, możesz użyć Hilta do wstrzykiwania ViewModel, aby uniknąć przekazywania zależności z funkcji kompozycyjnych wyższego rzędu.

Jeśli ViewModel ma zależności, viewModel() przyjmuje opcjonalny parametr ViewModelProvider.Factory.

Więcej informacji o ViewModel w Compose i o tym, jak używane są instancje w bibliotece Navigation Compose, a także o aktywnościach i fragmentach znajdziesz w dokumentacji dotyczącej interoperacyjności.

Strumienie danych

Compose ma rozszerzenia do najpopularniejszych rozwiązań opartych na strumieniach w Androidzie. Każde z tych rozszerzeń jest dostarczane przez inny artefakt:

Te artefakty rejestrują się jako odbiorcy i przedstawiają wartości jako State. Za każdym razem, gdy zostanie wyemitowana nowa wartość, Compose ponownie skomponuje te części interfejsu, w których używana jest ta state.value. Na przykład w tym kodzie ShowData jest ponownie komponowany za każdym razem, gdy exampleLiveData emituje nową wartość.

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

Operacje asynchroniczne w Compose

Jetpack Compose umożliwia wykonywanie operacji asynchronicznych za pomocą korutyn w funkcjach kompozycyjnych.

Więcej informacji o interfejsach API LaunchedEffect, produceStaterememberCoroutineScope znajdziesz w dokumentacji dotyczącej efektów ubocznych.

Komponent nawigacji obsługuje aplikacje Jetpack Compose. Więcej informacji znajdziesz w artykułach Nawigacja za pomocą ComposeMigracja z Jetpack Navigation na Navigation Compose.

Hilt

Hilt to zalecane rozwiązanie do wstrzykiwania zależności w aplikacjach na Androida, które bezproblemowo współpracuje z Compose.

Funkcja viewModel() wspomniana w sekcji ViewModel automatycznie używa obiektu ViewModel, który Hilt tworzy za pomocą adnotacji @HiltViewModel. Udostępniliśmy dokumentację z informacjami o integracji Hilt z ViewModel.

@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 i nawigacja

Hilt jest też zintegrowany z biblioteką Navigation Compose. Dodaj do pliku Gradle te dodatkowe zależności:

Groovy

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

Kotlin

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

Podczas korzystania z Navigation Compose zawsze używaj funkcji hiltViewModel composable do uzyskiwania instancji @HiltViewModel z adnotacjami ViewModel. Działa to w przypadku fragmentów lub aktywności, które są oznaczone adnotacjami @AndroidEntryPoint.

Jeśli na przykład ExampleScreen jest miejscem docelowym na wykresie nawigacji, wywołaj hiltViewModel(), aby uzyskać instancję ExampleViewModel w zakresie miejsca docelowego, jak pokazano we fragmencie kodu poniżej:

// 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)
        }
        /* ... */
    }
}

Jeśli zamiast tego chcesz pobrać instancję ViewModel w zakresie tras nawigacji lub grafu nawigacji, użyj funkcji kompozycyjnej hiltViewModel i przekaż odpowiedni element backStackEntry jako parametr:

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

Paging

Biblioteka Paging ułatwia stopniowe wczytywanie danych i jest obsługiwana w Compose. Na stronie wersji Paging znajdziesz informacje o dodatkowej zależności paging-compose, którą należy dodać do projektu, oraz jej wersji.

Oto przykład interfejsów API Compose biblioteki Paging:

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

Więcej informacji o używaniu biblioteki Paging w Compose znajdziesz w dokumentacji dotyczącej list i siatek.

Mapy

Aby udostępniać Mapy Google w aplikacji, możesz użyć biblioteki Maps Compose. Oto przykład użycia:

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