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 podrzędnej Activity ComponentActivity, która udostępnia 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 działania

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 prośby o uprawnienia na czas wykonywania, korzystając z RequestPermissionkontraktu dotyczącego pojedynczego uprawnienia lub RequestMultiplePermissionskontraktu dotyczącego wielu uprawnień.

Biblioteka zestawu uprawnień może być też używana jako warstwa 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 w systemie

Aby zapewnić niestandardową nawigację wstecz i zastąpić domyślne zachowanie przycisku Wstecz systemu w komponentach, komponent może używać elementu BackHandler do przechwytywania tego zdarzenia:

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, 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ące ViewModel lub tworzy nowe. Domyślnie zwracany parametr ViewModel jest ograniczony do obejmującej go aktywności, fragmentu lub miejsca docelowego nawigacji 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 składanego komponentu wywoływanego z aktywności, fragmentu lub miejsca docelowego w grafu nawigacji. Dzieje się tak, ponieważ ViewModels są domyślnie ograniczone do obiektów na poziomie ekranu. Dowiedz się więcej o cyklu życia i zakresie ViewModel.

Staraj się nie przekazywać instancji ViewModel innym elementom składanym, ponieważ może to utrudnić ich testowanie i spowodować błąd w podglądzie. 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, ale pamiętaj o cyklu życia i zakresie komponentu ViewModel. Jeśli kompozyt jest samowystarczalny, warto użyć Hilt, aby wstrzyknąć ViewModel, aby uniknąć przekazywania zależności z kompozytów nadrzędnych.

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. 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 ViewModel w Hilt.

@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 annotacją ViewModel. Ta funkcja działa z fragmentami lub aktywnościami oznaczonymi 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 strony z przekierowaniami 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 strony z formularzem w trybie edycji, zapoznaj się z dokumentacją dotyczącą list i kratek.

Mapy

Aby udostępnić 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"
        )
    }
}