Jetpack Compose znacznie ułatwia projektowanie i tworzenie interfejsu aplikacji. Compose przekształca stan w elementy interfejsu za pomocą:
- Kompozycja elementów
- Układ elementów
- Rysunek elementów
Ten dokument skupia się na układzie elementów i wyjaśnia niektóre elementy składowe, które Compose udostępnia, aby pomóc Ci w rozmieszczaniu elementów interfejsu.
Cele układów w Compose
Implementacja systemu układu w Jetpack Compose ma 2 główne cele:
- Wysoka wydajność
- możliwość łatwego tworzenia niestandardowych układów,
Podstawowe informacje o funkcjach typu „composable”
Funkcje typu „composable” to podstawowe elementy składowe Compose. Funkcja typu „composable” to funkcja, która emituje Unit opisujący część interfejsu. Funkcja pobiera dane wejściowe i generuje to, co jest wyświetlane na ekranie. Więcej informacji o funkcjach kompozycyjnych znajdziesz w dokumentacji Model mentalny Compose.
Funkcja typu „composable” może emitować kilka elementów interfejsu. Jeśli jednak nie określisz, jak mają być rozmieszczone, Compose może je ułożyć w sposób, który Ci nie odpowiada. Na przykład ten kod generuje 2 elementy tekstowe:
@Composable fun ArtistCard() { Text("Alfred Sisley") Text("3 minutes ago") }
Bez wskazówek dotyczących rozmieszczenia Compose układa elementy tekstowe jeden na drugim, przez co są nieczytelne:
Compose udostępnia kolekcję gotowych układów, które ułatwiają rozmieszczanie elementów interfejsu, oraz ułatwia definiowanie własnych, bardziej wyspecjalizowanych układów.
Komponenty układu standardowego
W wielu przypadkach możesz po prostu użyć standardowych elementów układu Compose.
Użyj Column, aby umieścić elementy na ekranie w pionie.
@Composable fun ArtistCardColumn() { Column { Text("Alfred Sisley") Text("3 minutes ago") } }
Podobnie możesz użyć Row, aby umieścić elementy na ekranie w poziomie. Zarówno Column, jak i Row obsługują konfigurowanie wyrównania elementów, które zawierają.
@Composable fun ArtistCardRow(artist: Artist) { Row(verticalAlignment = Alignment.CenterVertically) { Image(bitmap = artist.image, contentDescription = "Artist image") Column { Text(artist.name) Text(artist.lastSeenOnline) } } }
Użyj Box, aby umieścić elementy jeden na drugim. Box obsługuje też konfigurowanie określonego wyrównania elementów, które zawiera.
@Composable fun ArtistAvatar(artist: Artist) { Box { Image(bitmap = artist.image, contentDescription = "Artist image") Icon(Icons.Filled.Check, contentDescription = "Check mark") } }
Często te elementy składowe są wszystkim, czego potrzebujesz. Możesz napisać własną funkcję typu „composable”, aby połączyć te układy w bardziej rozbudowany układ, który pasuje do Twojej aplikacji.
Aby ustawić pozycję elementów podrzędnych w komponencie Row, ustaw argumenty horizontalArrangement i verticalAlignment. W przypadku Column ustaw argumenty verticalArrangement i horizontalAlignment:
@Composable fun ArtistCardArrangement(artist: Artist) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.End ) { Image(bitmap = artist.image, contentDescription = "Artist image") Column { /*...*/ } } }
Model układu
W modelu układu drzewo interfejsu jest układane w jednym przebiegu. Każdy węzeł jest najpierw proszony o zmierzenie siebie, a następnie o rekurencyjne zmierzenie wszystkich elementów podrzędnych, przekazując ograniczenia rozmiaru w dół drzewa do elementów podrzędnych. Następnie określane są rozmiary i położenie węzłów liści, a rozwiązane rozmiary i instrukcje dotyczące położenia są przekazywane z powrotem w górę drzewa.
Krótko mówiąc, elementy nadrzędne są mierzone przed elementami podrzędnymi, ale ich rozmiar i położenie są określane po elementach podrzędnych.
Rozważ następującą funkcję SearchResult.
@Composable fun SearchResult() { Row { Image( // ... ) Column { Text( // ... ) Text( // ... ) } } }
Ta funkcja generuje to drzewo interfejsu.
SearchResult
Row
Image
Column
Text
Text
W przykładzie SearchResult układ drzewa UI ma taką kolejność:
- Węzeł główny
Rowjest proszony o pomiar. - Węzeł główny
Rowprosi swój pierwszy element podrzędnyImageo pomiar. Imagejest węzłem liścia (czyli nie ma elementów podrzędnych), więc raportuje rozmiar i zwraca instrukcje dotyczące miejsca docelowego.- Węzeł główny
Rowprosi o pomiar drugi element podrzędny,Column. - Węzeł
Columnprosi swój pierwszy element podrzędnyTexto pomiar. - Pierwszy węzeł
Textjest węzłem-liściem, więc raportuje rozmiar i zwraca instrukcje dotyczące miejsca docelowego. - Węzeł
Columnprosi swój drugi węzeł podrzędnyTexto pomiar. - Drugi węzeł
Textjest węzłem-liściem, więc raportuje rozmiar i zwraca instrukcje dotyczące miejsca docelowego. - Teraz, gdy węzeł
Columnzmierzył, określił rozmiar i umieścił swoje elementy podrzędne, może określić własny rozmiar i położenie. - Teraz, gdy węzeł główny
Rowzmierzył, określił rozmiar i umieścił swoje elementy podrzędne, może określić swój rozmiar i położenie.
Wydajność
Compose osiąga wysoką wydajność, mierząc elementy podrzędne tylko raz. Pomiar jednoprzebiegowy jest korzystny dla wydajności, ponieważ umożliwia Compose efektywne obsługiwanie głębokich drzew interfejsu. Jeśli element mierzy swój element podrzędny dwukrotnie, a ten element podrzędny mierzy każdy ze swoich elementów podrzędnych dwukrotnie itd., pojedyncza próba ułożenia całego interfejsu wymagałaby dużo pracy, co utrudniłoby utrzymanie wydajności aplikacji.
Jeśli z jakiegoś powodu układ wymaga wielu pomiarów, Compose oferuje specjalny system pomiarów wewnętrznych. Więcej informacji o tej funkcji znajdziesz w artykule Pomiary wewnętrzne w układach Compose.
Ponieważ pomiar i umieszczanie to odrębne podetapy etapu układu, wszelkie zmiany, które wpływają tylko na umieszczanie elementów, a nie na pomiar, można wykonywać osobno.
Używanie modyfikatorów w układach
Jak opisano w artykule Modyfikatory w Compose, możesz używać modyfikatorów do dekorowania lub rozszerzania funkcji kompozycyjnych. Modyfikatory są niezbędne
do dostosowywania układu. Na przykład tutaj łączymy kilka modyfikatorów, aby dostosować ArtistCard:
@Composable fun ArtistCardModifiers( artist: Artist, onClick: () -> Unit ) { val padding = 16.dp Column( Modifier .clickable(onClick = onClick) .padding(padding) .fillMaxWidth() ) { Row(verticalAlignment = Alignment.CenterVertically) { /*...*/ } Spacer(Modifier.size(padding)) Card( elevation = CardDefaults.cardElevation(defaultElevation = 4.dp), ) { /*...*/ } } }
W kodzie powyżej zwróć uwagę na różne funkcje modyfikatora używane razem.
clickablesprawia, że element kompozycyjny reaguje na dane wejściowe użytkownika i wyświetla efekt fali.paddingdodaje odstęp wokół elementu.fillMaxWidthsprawia, że element kompozycyjny wypełnia maksymalną szerokość nadaną mu przez element nadrzędny.size()określa preferowaną szerokość i wysokość elementu.
Układy z możliwością przewijania
Więcej informacji o układach przewijanych znajdziesz w dokumentacji dotyczącej gestów w Compose.
Informacje o listach i listach leniwych znajdziesz w dokumentacji dotyczącej list w Compose.
Elastyczne układy stron
Układ powinien być zaprojektowany z uwzględnieniem różnych orientacji ekranu i rozmiarów urządzeń. Compose oferuje kilka gotowych mechanizmów, które ułatwiają dostosowywanie układów kompozycyjnych do różnych konfiguracji ekranu.
Ograniczenia
Aby poznać ograniczenia pochodzące z elementu nadrzędnego i odpowiednio zaprojektować układ, możesz użyć BoxWithConstraints. Ograniczenia pomiaru można znaleźć w zakresie funkcji lambda treści. Możesz użyć tych ograniczeń pomiaru, aby utworzyć różne układy dla różnych konfiguracji ekranu:
@Composable fun WithConstraintsComposable() { BoxWithConstraints { Text("My minHeight is $minHeight while my maxWidth is $maxWidth") } }
Układy oparte na boksach
Compose udostępnia wiele różnych funkcji kompozycyjnych opartych na Material Design z zależnością androidx.compose.material:material (uwzględnianą podczas tworzenia projektu Compose w Android Studio), co ułatwia tworzenie interfejsu. Dostępne są takie elementy jak Drawer, FloatingActionButton i TopAppBar.
Komponenty Material Design intensywnie korzystają z interfejsów API gniazd, czyli wzorca wprowadzonego przez Compose, który umożliwia dostosowywanie funkcji kompozycyjnych. Dzięki temu komponenty są bardziej elastyczne, ponieważ akceptują element podrzędny, który może się sam skonfigurować, zamiast ujawniać każdy parametr konfiguracji elementu podrzędnego.
Miejsca pozostawiają puste miejsce w interfejsie, które deweloper może wypełnić według własnego uznania. Oto przykładowe boksy, które możesz dostosować w TopAppBar:
Komponenty zazwyczaj przyjmują funkcję kompozycyjną lambda content ( content: @Composable
() -> Unit). Interfejsy API gniazd udostępniają wiele parametrów content do konkretnych zastosowań.
Na przykład TopAppBar umożliwia podanie treści dla title, navigationIcon i actions.
Na przykład Scaffold umożliwia wdrożenie interfejsu z podstawową strukturą układu Material Design. Scaffold udostępnia miejsca na najpopularniejsze komponenty Material najwyższego poziomu, takie jak TopAppBar, BottomAppBar, FloatingActionButton i Drawer. Dzięki Scaffold możesz łatwo upewnić się, że te komponenty są prawidłowo rozmieszczone i właściwie ze sobą współpracują.
@Composable fun HomeScreen(/*...*/) { ModalNavigationDrawer(drawerContent = { /* ... */ }) { Scaffold( topBar = { /*...*/ } ) { contentPadding -> // ... } } }
Polecane dla Ciebie
- Uwaga: tekst linku jest wyświetlany, gdy język JavaScript jest wyłączony.
- Modyfikatory tworzenia
- Kotlin w Jetpack Compose
- Komponenty i układy Material Design