Jetpack Compose znacznie ułatwia projektowanie i tworzenie interfejsu użytkownika 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 omawia niektóre elementy składowe Compose, które ułatwiają rozmieszczanie elementów interfejsu użytkownika.
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. Funkcja składana to funkcja emitująca Unit
, która opisuje 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 układa je jeden na drugim, przez co stają się one nieczytelne:
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 orientacji pionowej.
@Composable fun ArtistCardColumn() { Column { Text("Alfred Sisley") Text("3 minutes ago") } }
Podobnie możesz użyć Row
, aby umieścić elementy poziomo na ekranie. Zarówno element Column
, jak i element Row
umożliwiają 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 określonego 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 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.
Rozważ 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:
- Wskazanie do pomiaru węzła głównego
Row
. - Węzeł główny
Row
prosi pierwszy element podrzędny,Image
, o zmierzenie. Image
to węzeł liścia (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 podrzędne, może określić swój rozmiar i położenie. - Teraz, gdy węzeł główny
Row
zmierzył i umieścił swoje elementy potomne, może 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 swoje dziecko dwa razy, a to dziecko zmierzy swoje dzieci dwa razy i tak dalej, jedno ułożenie całego interfejsu wymagałoby bardzo dużo pracy, co utrudnia 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 danych, 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 powyższym kodzie zwróć uwagę na różne funkcje modyfikujące używane razem.
clickable
sprawia, że komponent reaguje na działania 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 przewijalne,
Więcej informacji o przewijanych układach znajdziesz w dokumentacji gestów w komponowaniu wiadomości.
Więcej informacji o listach i listach leniwych 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 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 slotach
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 wdrożono w Compose, aby umożliwić dostosowanie komponentów składanych. 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 pozostawiają 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