Podstawy układu tworzenia wiadomości

Jetpack Compose ułatwia projektowanie i tworzenie UI aplikacji. Funkcja tworzenia zmienia stan na elementy interfejsu za pomocą:

  1. Skład elementów
  2. Układ elementów
  3. Rysowanie elementów

Utwórz kompozycję z przekształcaniem stanu na interfejs użytkownika za pomocą kompozycji, układu, rysowania

W tym dokumencie omawiamy układ elementów i wyjaśniamy niektóre z nich, jakie zawiera funkcja Compose, która pomaga rozmieszczać elementy interfejsu.

Cele układów w widoku tworzenia

Implementacja systemu układów w Jetpack Compose ma dwa główne cele:

Podstawy funkcji kompozycyjnych

Funkcje kompozycyjne to podstawowy element składowy funkcji Compose. Funkcja kompozycyjna to funkcja zwracająca wartość Unit, która opisuje część interfejsu użytkownika. Ta funkcja pobiera dane wejściowe i generuje informacje wyświetlane na ekranie. Więcej informacji na temat funkcji kompozycyjnych znajdziesz w dokumentacji modelu mentalnego tworzenia.

Funkcja kompozycyjna może emitować kilka elementów interfejsu. Jeśli jednak nie zapewnisz sposobu ich uporządkowania, funkcja tworzenia może rozmieścić elementy w niewłaściwy sposób. Na przykład ten kod generuje 2 elementy tekstowe:

@Composable
fun ArtistCard() {
    Text("Alfred Sisley")
    Text("3 minutes ago")
}

Jeśli nie otrzymasz wskazówek dotyczących kolejności elementów, w widoku wiadomości elementy tekstowe będą się nakładać na siebie, co sprawi, że będą nieczytelne:

2 elementy tekstowe rysowane jeden na drugim, przez co tekst jest nieczytelny;

Aplikacja Compose udostępnia kolekcję gotowych układów, które ułatwiają rozmieszczanie elementów interfejsu i ułatwiają definiowanie własnych, bardziej specjalistycznych układów.

Komponenty układu standardowego

W wielu przypadkach wystarczy po prostu użyć elementów układu standardowego tworzenia wiadomości.

Użyj Column, aby umieścić elementy w pionie na ekranie.

@Composable
fun ArtistCardColumn() {
    Column {
        Text("Alfred Sisley")
        Text("3 minutes ago")
    }
}

2 elementy tekstowe umieszczone w układzie kolumn w taki sposób, aby tekst był czytelny;

Aby umieścić elementy poziomo na ekranie, użyj wartości Row. Zarówno Column, jak i Row obsługują konfigurowanie wyrównania zawartych w nich elementów.

@Composable
fun ArtistCardRow(artist: Artist) {
    Row(verticalAlignment = Alignment.CenterVertically) {
        Image(bitmap = artist.image, contentDescription = "Artist image")
        Column {
            Text(artist.name)
            Text(artist.lastSeenOnline)
        }
    }
}

przedstawia bardziej złożony układ z małą grafiką obok kolumny elementów tekstowych;

Korzystając z Box, możesz umieszczać elementy nad innymi. Box obsługuje też konfigurowanie określonego wyrównania zawartych w niej elementów.

@Composable
fun ArtistAvatar(artist: Artist) {
    Box {
        Image(bitmap = artist.image, contentDescription = "Artist image")
        Icon(Icons.Filled.Check, contentDescription = "Check mark")
    }
}

Wyświetla 2 elementy umieszczone jeden nad drugim

Często tylko te elementy składowe są potrzebne. Możesz napisać własną funkcję kompozycyjną, aby połączyć te układy w bardziej skomplikowany układ, który pasuje do Twojej aplikacji.

Porównanie 3 prostych funkcji kompozycyjnych układu: column, row i box

Aby ustawić pozycję elementu podrzędnego w elemencie 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 { /*...*/ }
    }
}

Elementy są wyrównane do prawej

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 rekurencyjnie mierzy wszystkie elementy podrzędne, przekazując dzieciom ograniczenia rozmiaru drzewa. Następnie określamy rozmiar i rozmieszczanie węzłów liści, a oszacowane rozmiary i instrukcje dotyczące miejsca docelowego są przesyłane z powrotem do drzewa.

W skrócie rodzice mierzą się przed dziećmi, ale są dopasowywane i umieszczane po nich.

Przyjrzyj się tej funkcji SearchResult.

@Composable
fun SearchResult() {
    Row {
        Image(
            // ...
        )
        Column {
            Text(
                // ...
            )
            Text(
                // ...
            )
        }
    }
}

Ta funkcja generuje następujące drzewo interfejsu użytkownika.

SearchResult
  Row
    Image
    Column
      Text
      Text

W przykładzie SearchResult układ drzewa interfejsu ma taką kolejność:

  1. Węzeł główny Row jest objęty pomiarem.
  2. Węzeł główny Row prosi o dokonanie pomiaru przez swój pierwszy węzeł podrzędny, Image.
  3. Image to węzeł liścia (czyli nie ma elementów podrzędnych), więc zgłasza rozmiar i zwraca instrukcje umieszczania.
  4. Węzeł główny Row prosi o przeprowadzenie pomiaru drugiego węzła podrzędnego, Column.
  5. Węzeł Column prosi o przeprowadzenie pomiaru pierwszego elementu podrzędnego (Text).
  6. Pierwszy węzeł Text jest węzłem liścia, więc zgłasza rozmiar i zwraca instrukcje miejsca docelowego.
  7. Węzeł Column prosi o 2 podrzędny element podrzędny Text do przeprowadzenia pomiaru.
  8. Drugi węzeł Text jest węzłem liścia, więc zgłasza rozmiar i zwraca instrukcje miejsca docelowego.
  9. Gdy węzeł Column zmierzył, zmienił rozmiar i umieścił swoje elementy podrzędne, może określić swój rozmiar i miejsce docelowe.
  10. Gdy węzeł główny Row zmierzył, określił rozmiar i umieścił swoje elementy podrzędne, może określić swój rozmiar i miejsce docelowe.

Kolejność pomiarów, rozmiarów i rozmieszczenia w drzewie interfejsu wyników wyszukiwania

Wyniki

Tworzenie osiąga wysoką skuteczność, ponieważ mierzy dane podrzędne tylko raz. Pomiar jednoprzebiegowy dobrze zwiększa wydajność, ponieważ umożliwia usłudze Compose skuteczne obsługę głębokich drzew interfejsu. Jeśli dany element zmierzyłby swoje podrzędne dwa razy, a to dziecko dwa razy itd., pojedyncza próba utworzenia układu interfejsu musiałaby wymagać dużo pracy, co utrudniałoby utrzymanie wydajności aplikacji.

Jeśli z jakiegoś powodu układ wymaga wielu pomiarów, możesz użyć specjalnego systemu do obsługi pomiarów wewnętrznych. Więcej informacji o tej funkcji znajdziesz w artykule o pomiarach wewnętrznych w układach tworzenia wiadomości.

Pomiar i umiejscowienie to odrębne podfazy przekazywania układu, dlatego wszystkie zmiany, które mają wpływ tylko na rozmieszczenie elementów, a nie na pomiary, mogą być wprowadzane oddzielnie.

Używanie modyfikatorów w układach

Jak wspomnieliśmy w sekcji Modyfikatory tworzenia wiadomości, za pomocą tych modyfikatorów możesz dekorować i ulepszać kompozycje. Modyfikatory są niezbędne do dostosowywania układu. Na przykład tutaj łączy kilka modyfikatorów, aby dostosować element 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),
        ) { /*...*/ }
    }
}

Jeszcze bardziej złożony układ z zastosowaniem modyfikatorów do zmiany układu grafiki i tego, które obszary reagują na dane wejściowe użytkownika.

W kodzie powyżej zwróć uwagę na różne funkcje modyfikatorów stosowane razem.

  • clickable reaguje na dane wejściowe użytkownika i pokazuje fali.
  • padding umieszcza odstęp wokół elementu.
  • fillMaxWidth sprawia, że funkcja kompozycyjna wypełnia maksymalną szerokość, którą otrzymuje od elementu nadrzędnego.
  • size() określa preferowaną szerokość i wysokość elementu.

Układy z możliwością przewijania

Więcej informacji o układach z możliwością przewijania znajdziesz w dokumentacji gestów tworzenia.

Listy i leniwe listy znajdziesz w dokumentacji tworzenia list.

Układy elastyczne

Układ powinien być projektowany z uwzględnieniem różnych orientacji i rozmiarów ekranu. Funkcja tworzenia oferuje od razu kilka mechanizmów ułatwiających dostosowywanie układów kompozycyjnych do różnych konfiguracji ekranu.

Ograniczenia

Aby poznać ograniczenia pochodzące z elementu nadrzędnego i zaprojektować odpowiedni układ, możesz użyć właściwości BoxWithConstraints. Ograniczenia dotyczące pomiarów można znaleźć w zakresie funkcji lambda dotyczącej treści. Korzystając z tych ograniczeń pomiarowych, możesz tworzyć różne układy dostosowane do różnych konfiguracji ekranu:

@Composable
fun WithConstraintsComposable() {
    BoxWithConstraints {
        Text("My minHeight is $minHeight while my maxWidth is $maxWidth")
    }
}

Układy oparte na boksach

Narzędzie Compose udostępnia szeroką gamę funkcji kompozycyjnych opartych na Material Design z zależność androidx.compose.material:material (która jest dostępna podczas tworzenia projektu tworzenia w Android Studio), co ułatwia tworzenie interfejsu. Dostępne są takie elementy jak Drawer, FloatingActionButton i TopAppBar.

Komponenty Material Design intensywnie wykorzystują interfejsy API boksów reklamowych. Wprowadzany jest wzór do tworzenia wiadomości, który dodaje warstwę dostosowywania do elementów kompozycyjnych. Takie podejście zwiększa elastyczność komponentów, ponieważ przyjmują element podrzędny, który można skonfigurować samodzielnie, zamiast ujawniać każdy parametr konfiguracji elementu podrzędnego. Pozostawiają one puste miejsce w interfejsie, które deweloper może wypełnić według własnego uznania. Są to na przykład przedziały, które możesz dostosować w elemencie TopAppBar:

Diagram pokazujący dostępne przedziały na pasku aplikacji Material Komponenty

Elementy kompozycyjne zwykle korzystają z funkcji lambda kompozycyjnej content ( content: @Composable () -> Unit). Interfejsy API przedziałów udostępniają wiele parametrów content do konkretnych zastosowań. Na przykład TopAppBar umożliwia podzielenie się treściami do title, navigationIcon i actions.

Na przykład Scaffold umożliwia wdrożenie interfejsu użytkownika z podstawową strukturą układu Material Design. ScaffoldUdostępnia boksy na najpopularniejsze komponenty Material najwyższego poziomu, takie jak TopAppBar, BottomAppBar, FloatingActionButton i Drawer. Używając atrybutu Scaffold, można łatwo zadbać o to, aby komponenty te były prawidłowo umiejscowione i prawidłowo ze sobą współdziałały.

Przykładowa aplikacja JetNews, która używa Scaffold do umieszczania wielu elementów

@Composable
fun HomeScreen(/*...*/) {
    ModalNavigationDrawer(drawerContent = { /* ... */ }) {
        Scaffold(
            topBar = { /*...*/ }
        ) { contentPadding ->
            // ...
        }
    }
}