Tworzenie i inne biblioteki

W sekcji „Tworzenie” możesz korzystać z ulubionych bibliotek. Z tej sekcji dowiesz się, jak włączyć kilka najprzydatniejszych bibliotek.

Aktywność

Aby użyć Compose w aktywności, musisz użyć klasy ComponentActivity, która jest podklasą klasy Activity i zawiera odpowiednie komponenty LifecycleOwner dla Compose. Udostępnia też dodatkowe interfejsy API, które odłączają 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ą rekompozycję i zostają prawidłowo usunięte, jeśli kompozyt został usunięty z kompozycji.

Wynik aktywności

Interfejs API rememberLauncherForActivityResult() umożliwia uzyskanie wyniku z działalności w komponowalnym:

@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 pokazuje prosty GetContent()kontrakt. Kliknięcie przycisku powoduje wysłanie prośby. Lambda końcowa dla rememberLauncherForActivityResult() jest wywoływana, gdy użytkownik wybierze obraz i powróci do aktywności uruchamiającej. Spowoduje to załadowanie wybranego obrazu za pomocą funkcji rememberImagePainter() Coil.

Jako pierwszy argument funkcji rememberLauncherForActivityResult() można użyć dowolnej podklasy typu ActivityResultContract. Oznacza to, że możesz użyć tej techniki, aby poprosić o treści z ramy, a także w innych typowych wzorach. Możesz też tworzyć własne kontrakty niestandardowe i stosować je za pomocą tej metody.

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

Tego samego interfejsu Activity Result API i rememberLauncherForActivityResult(), o których mowa powyżej, można używać do wysyłania żądań uprawnień na czas działania, korzystając z RequestPermissionuprawnień dla pojedynczego uprawnienia lub RequestMultiplePermissionsuprawnień dla wielu uprawnień.

Bibliotekę Accompanist Permissions można też używać jako warstwy nad tymi interfejsami API, aby mapować bieżący stan przyznanych uprawnień na stan, którego może używać interfejs użytkownika Compose.

Obsługa przycisku Wstecz systemu

Aby zapewnić niestandardową nawigację wstecz i zastąpić domyślne zachowanie przycisku Wstecz w systemie, komponent może użyć elementu BackHandler, aby przechwycić to zdarzenie:

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

Pierwszy argument określa, czy moduł BackHandler jest obecnie włączony. Możesz użyć tego argumentu, aby tymczasowo wyłączyć moduł obsługi na podstawie stanu komponentu. Lambda końcowa zostanie wywołana, jeśli użytkownik wywoła zdarzenie systemowe wsteczne, 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, wywołując funkcję viewModel(). Dodaj do pliku Gradle te zależności:

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 kodze funkcji viewModel().

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

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

viewModel() zwraca istniejący element ViewModel lub tworzy nowy. Domyślnie zwracany parametr ViewModel jest ograniczony do aktywności, fragmentu lub miejsca docelowego nawigacji, które je otaczają, i jest przechowywany tak długo, jak długo istnieje zakres.

Jeśli na przykład kompozyt jest używany w aktywności, viewModel() zwraca tę samą instancję do czasu 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

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

Staraj się nie przekazywać instancji ViewModel innym elementom składanym, ponieważ może to utrudnić ich testowanie i spowodować problemy z podglądem. Zamiast tego jako parametrów podawaj tylko dane i funkcje, których te dane potrzebują.

Możesz używać instancji ViewModel do zarządzania stanem komponentów na poziomie podrzędnego ekranu, pamiętaj jednak o cyklu życia i zakresie komponentu ViewModel. Jeśli kompozyt jest samowystarczalny, możesz użyć Hilt, aby wstrzyknąć ViewModel, aby uniknąć przekazywania zależności od nadrzędnych kompozytów.

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

Więcej informacji o komponencie ViewModel w Compose oraz o tym, jak używać instancji z biblioteką Compose nawigacji lub aktywności i fragmentów znajdziesz w dokumentacji dotyczącej interoperacyjności.

strumienie danych,

Compose zawiera rozszerzenia dla najpopularniejszych na Androidzie rozwiązań opartych na strumieniu. Każde z tych rozszerzeń jest dostarczane przez inny element:

Te elementy rejestrują się jako słuchacze i reprezentują wartości jako State. Za każdym razem, gdy emitowana jest nowa wartość, Compose ponownie tworzy te części interfejsu, w których używana jest zmienna state.value. Na przykład w tym kodzie funkcja ShowData jest ponownie wykonywana za każdym razem, gdy funkcja exampleLiveData wyemituje 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 sekcji Compose

Jetpack Compose umożliwia wykonywanie operacji asynchronicznych za pomocą coroutines w ramach komponentów.

Więcej informacji znajdziesz w dokumentacji interfejsów API LaunchedEffect, produceStaterememberCoroutineScopedokumentacji poświęconej skutkom ubocznym.

Komponent Nawigacja obsługuje aplikacje Jetpack Compose. Więcej informacji znajdziesz w artykule Przechodzenie między elementami za pomocą ComposePrzenoszenie nawigacji Jetpacka do Compose.

Hilt

Hilt to zalecane rozwiązanie do wstrzykiwania zależności w aplikacjach na Androida. Działa ono bezproblemowo z Compose.

Funkcja viewModel() wymieniona w sekcji ViewModel automatycznie używa ViewModel utworzonego przez Hilt 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 integruje się też 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 komponentu Nawigacja zawsze używaj funkcji hiltViewModel, aby uzyskać instancję @HiltViewModel z annotacjami ViewModel. Funkcja działa w przypadku fragmentów lub aktywności oznaczonych znacznikiem @AndroidEntryPoint.

Jeśli na przykład ExampleScreen to punkt docelowy w grafie nawigacji, wywołaj funkcję hiltViewModel(), aby uzyskać instancję ExampleViewModel ograniczoną do punktu docelowego, jak pokazano w tym fragmencie kodu:

// 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 chcesz pobrać instancję funkcji ViewModel ograniczoną do tras nawigacyjnych lub grafu nawigacyjnego, użyj funkcji składanej hiltViewModel i przekaż jako parametr odpowiednią funkcję backStackEntry:

// 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. Strona wersji zawiera informacje o dodatkowej zależności paging-compose, którą należy dodać do projektu i jego wersji.

Oto przykład interfejsów 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")
        }
    }
}

Aby dowiedzieć się więcej o używaniu pagingu w Compose, zapoznaj się z dokumentacją dotyczącą list i kratek.

Mapy

Aby udostępnić Mapy Google w aplikacji, możesz użyć biblioteki Mapy – 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"
        )
    }
}