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
- Rysowanie elementów
Ten dokument skupia się na rozmieszczeniu elementów i wyjaśnia niektóre z elementów konstrukcyjnych, które Compose udostępnia, aby ułatwić układanie elementów interfejsu.
Cele układów w polu tworzenia wiadomości
Implementacja systemu układu w Jetpack Compose ma 2 główne cele:
- Wysoka wydajność
- Możliwość łatwego tworzenia niestandardowych układów
Podstawy funkcji składanych
Funkcje składane to podstawowy element Compose. Funkcje składane to funkcje emitujące Unit
, które opisują część interfejsu użytkownika. Funkcja ta przyjmuje dane wejściowe i generuje to, co jest widoczne na ekranie. Więcej informacji o komponowalnych znajdziesz w dokumentacji dotyczącej modelu mentalnego kompozytowalności.
Funkcja składana może emitować kilka elementów interfejsu użytkownika. Jeśli jednak nie podasz wskazówek dotyczących ich rozmieszczenia, Compose może umieścić elementy w nieodpowiednim miejscu. Ten kod generuje na przykład 2 elementy tekstowe:
@Composable fun ArtistCard() { Text("Alfred Sisley") Text("3 minutes ago") }
Bez wskazówek dotyczących układu elementów tekstowych Compose ułoży je jeden na drugim, co utrudni ich odczytanie:
Compose udostępnia kolekcję gotowych układów, które ułatwiają rozmieszczanie elementów interfejsu użytkownika. Dzięki temu możesz łatwo definiować własne, bardziej specjalistyczne układy.
Komponenty standardowego układu
W wielu przypadkach możesz po prostu użyć standardowych elementów układu w 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żywać Row
do umieszczania elementów na ekranie w poziomie. Zarówno element Column
, jak i element 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) } } }
Aby umieścić elementy jeden na drugim, użyj Box
. Box
obsługuje też konfigurowanie dokładnego wyrównania elementów.
@Composable fun ArtistAvatar(artist: Artist) { Box { Image(bitmap = artist.image, contentDescription = "Artist image") Icon(Icons.Filled.Check, contentDescription = "Check mark") } }
Często te elementy są wszystkim, czego potrzebujesz. Możesz napisać własną funkcję składającą się z elementów, aby połączyć te układy w bardziej rozbudowany układ odpowiedni dla Twojej aplikacji.
Aby ustawić pozycję elementów potomnych w elementach Row
, użyj argumentów horizontalArrangement
i verticalAlignment
. W przypadku funkcji 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 przejeździe. Każdy węzeł jest najpierw proszony o zmierzenie siebie, a potem rekurencyjnie o zmierzenie wszystkich podrzędnych węzłów, przekazując ograniczenia rozmiaru w drzewie do podrzędnych węzłów. Następnie określa się rozmiary i miejsce umieszczenia węzłów liści, a rozwiązane rozmiary i instrukcje dotyczące umieszczenia są przekazywane z powrotem do drzewa.
Krótko mówiąc, rodzice mierzą się przed dziećmi, ale ich rozmiar i miejsce na rysunku są dobierane po ich dzieciach.
Weź pod uwagę funkcję SearchResult
.
@Composable fun SearchResult() { Row { Image( // ... ) Column { Text( // ... ) Text( // ... ) } } }
Ta funkcja zwraca to drzewo interfejsu użytkownika.
SearchResult
Row
Image
Column
Text
Text
W przykładzie SearchResult
układ drzewa interfejsu użytkownika jest następujący:
- Węzeł główny
Row
prosi o zmierzenie. - Węzeł główny
Row
prosi pierwszy element podrzędny,Image
, o zmierzenie. Image
to liść (czyli nie ma żadnych elementów podrzędnych), więc podaje rozmiar i zwraca instrukcje dotyczące umieszczenia.- Węzeł główny
Row
prosi swoje drugie dziecko,Column
, o zmierzenie. - Węzeł
Column
prosi pierwszy element podrzędnyText
o zmierzenie. - Pierwszy węzeł
Text
jest węzłem-liściem, więc podaje rozmiar i zwraca instrukcje dotyczące umieszczenia. - Węzeł
Column
prosi swoje drugie dzieckoText
o pomiar. - Drugi węzeł
Text
jest węzłem-liściem, więc podaje rozmiar i zwraca instrukcje dotyczące umieszczenia. - Teraz, gdy węzeł
Column
zmierzył i umieścił swoje elementy potomne, może określić swój rozmiar i położenie. - Teraz, gdy węzeł
Row
zmierzono, ustawiono jego rozmiar i umieszczono jego potomków, może on określić swój rozmiar i położenie.
Wydajność
Compose osiąga wysoką wydajność, mierząc podrzędne tylko raz. Pomiar w pojedynczym przejściu pozytywnie wpływa na wydajność, ponieważ pozwala Compose sprawnie obsługiwać głębokie drzewa interfejsu użytkownika. Jeśli element zmierzy swój element potomny 2 razy, a ten zmierzy swoje potomne elementy 2 razy i tak dalej, jedno ułożenie całego interfejsu będzie wymagało dużo pracy, co utrudni utrzymanie wydajności aplikacji.
Jeśli układ wymaga z jakiegoś powodu kilku pomiarów, Compose oferuje specjalny system pomiarów wewnętrznych. Więcej informacji o tej funkcji znajdziesz w artykule Własne pomiary w układance w Compose.
Ponieważ pomiar i rozmieszczenie to odrębne etapy przepływu układu, wszelkie zmiany, które wpływają tylko na umieszczanie elementów, a nie na pomiar, mogą być wykonywane osobno.
Używanie modyfikatorów w układach
Jak opisano w sekcji Modyfikatory kompozytowanych elementów, możesz używać modyfikatorów do ozdabiania i uzupełniania kompozytowanych elementów. Modyfikatory są niezbędne do dostosowywania układu. Na przykład tutaj łańcuchujemy 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 modyfikujące używane razem.
clickable
sprawia, że komponent reaguje na dane wejściowe użytkownika i wyświetla efekt falowania.padding
umieszcza odstęp wokół elementu.fillMaxWidth
powoduje, że kompozyt wypełnia maksymalną szerokość określoną przez jego nadrzędny element.size()
określa preferowaną szerokość i wysokość elementu.
Układy z możliwością przewijania
Więcej informacji o przewijanych układach znajdziesz w dokumentacji gestów podczas tworzenia wiadomości.
Więcej informacji o listach i lista z opóźnionym wczytywaniem znajdziesz w dokumentacji dotyczącej tworzenia list.
Układy elastyczne
Układ powinien być zaprojektowany z uwzględnieniem różnych orientacji ekranu i rozmiarów. Compose oferuje kilka wbudowanych mechanizmów, które ułatwiają dostosowanie układów kompozytowych 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 pomiarów można znaleźć w zakresie lambda treści. Za pomocą tych ograniczeń pomiarowych możesz tworzyć 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 przedziałach
Compose udostępnia wiele komponentów opartych na Material Design z zależnością androidx.compose.material:material
(dostępną podczas tworzenia projektu Compose w Android Studio), aby ułatwić tworzenie interfejsu użytkownika. Dostępne są elementy Drawer
, FloatingActionButton
i TopAppBar
.
Komponenty Material Design intensywnie korzystają z interfejsów API slotów, czyli wzoru, który Compose wprowadza, aby umożliwić dostosowanie na poziomie komponentów. Dzięki temu komponenty są bardziej elastyczne, ponieważ mogą przyjmować element podrzędny, który może konfigurować się samodzielnie, zamiast udostępniać wszystkie parametry konfiguracji elementu podrzędnego.
Miejsca w interfejsie to puste miejsce, które deweloper może wypełnić według własnego uznania. Na przykład w usługach TopAppBar
możesz dostosować te sloty:
Elementy składane zwykle korzystają z komponowalnego interfejsu lambda content
( content: @Composable
() -> Unit
). Interfejsy API slotów udostępniają wiele parametrów content
do określonych zastosowań.
Na przykład TopAppBar
umożliwia Ci udostępnianie treści w przypadku title
,
navigationIcon
i actions
.
Na przykład:
Scaffold
pozwala na implementację interfejsu z podstawową strukturą układu Material Design.
Scaffold
zapewnia miejsca na najczęściej używane komponenty Material na najwyższym poziomie, takie jak TopAppBar
,
BottomAppBar
,
FloatingActionButton
i Drawer
. Dzięki użyciu komponentu Scaffold
możesz łatwo sprawdzić, czy te komponenty są prawidłowo umieszczone i czy współpracują ze sobą.
@Composable fun HomeScreen(/*...*/) { ModalNavigationDrawer(drawerContent = { /* ... */ }) { Scaffold( topBar = { /*...*/ } ) { contentPadding -> // ... } } }
Polecane dla Ciebie
- Uwaga: tekst linku jest wyświetlany, gdy obsługa JavaScript jest wyłączona
- Tworzenie modyfikatorów
- Kotlin w Jetpack Compose
- Komponenty i układy w materiałach