Podstawy układu tworzenia wiadomości

Jetpack Compose znacznie ułatwia projektowanie i tworzenie interfejsu użytkownika aplikacji. Compose przekształca stan w elementy interfejsu za pomocą:

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

Tworzenie stanu przekształcania w UI za pomocą kompozycji, układu i rysowania

Ten dokument skupia się na układzie elementów i objaśnia niektóre elementy składowe dostępne w usłudze Compose, aby pomóc Ci rozmieścić elementy interfejsu.

Cele układów w polu tworzenia wiadomości

Implementacja systemu układu w Jetpack Compose ma 2 główne cele:

Podstawowe informacje o funkcjach kompozycyjnych

Funkcje składane to podstawowy element Compose. Funkcja kompozycyjna to funkcja wysyłająca obiekt Unit, który 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 kompozytowania.

Funkcja kompozycyjna może generować kilka elementów interfejsu. Jeśli jednak nie doradzisz, jak je rozmieścić, funkcja Utwórz może uporządkować elementy w sposób, który Ci nie odpowiada. 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:

Dwa elementy tekstowe nałożone na siebie, przez co tekst jest nieczytelny

Compose zawiera kolekcję gotowych do użycia układów, które pomagają rozmieszczać elementy interfejsu i ułatwiają definiowanie własnych, bardziej specjalistycznych układów.

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 pionowo na ekranie.

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

Dwa elementy tekstowe ułożone w kolumnie, aby tekst był czytelny

Podobnie, aby umieścić elementy poziomo na ekranie, użyj Row. 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)
        }
    }
}

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

Użyj narzędzia Box, aby umieścić elementy na innym. 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")
    }
}

Dwa elementy ułożone jeden na drugim

Często te elementy są wszystkim, czego potrzebujesz. Możesz napisać własną funkcję kompozycyjną, aby połączyć te układy w bardziej skomplikowany układ pasujący do Twojej aplikacji.

Porównuje 3 proste komponenty układu: kolumnę, wiersz i pole

Aby ustawić pozycję elementu podrzędnego w elemencie Row, ustaw argumenty horizontalArrangement i verticalAlignment. W przypadku Column ustaw argumenty verticalArrangementhorizontalAlignment:

@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 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 pozycja są określane 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:

  1. Wskaźnik jest żądany dla węzła głównego Row.
  2. Węzeł główny Row prosi pierwszy element podrzędny, Image, o zmierzenie.
  3. Image to węzeł liścia (czyli nie ma żadnych elementów podrzędnych), więc podaje rozmiar i zwraca instrukcje dotyczące umieszczenia.
  4. Węzeł główny Row prosi swoje drugie dziecko, Column, o zmierzenie.
  5. Węzeł Column prosi pierwszy element podrzędny Text o zmierzenie.
  6. Pierwszy węzeł Text jest węzłem-liściem, więc podaje rozmiar i zwraca instrukcje dotyczące umieszczenia.
  7. Węzeł Column prosi swoje drugie dziecko Text o pomiar.
  8. Drugi węzeł Text jest węzłem liścia, więc zgłasza rozmiar i zwraca instrukcje miejsca docelowego.
  9. Teraz, gdy węzeł Column zmierzył i umieścił swoje elementy podrzędne, może określić swój rozmiar i położenie.
  10. 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.

kolejność pomiarów, rozmiarów i miejsc docelowych w drzewie interfejsu wyników wyszukiwania;

Wydajność

Tworzenie wiadomości osiąga wysoką skuteczność dzięki mierzeniu aktywności dzieci 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 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 wspomnieliśmy w artykule Modyfikatory tworzenia wiadomości, możesz ich używać do dekorowania lub rozszerzania swoich elementów kompozycyjnych. 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),
        ) { /*...*/ }
    }
}

jeszcze bardziej złożony układ, w którym za pomocą modyfikatorów można zmieniać sposób rozmieszczania grafik i określać, które obszary mają reagować na działania użytkownika;

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

Przy projektowaniu układu należy brać pod uwagę różne orientacje ekranu i rozmiary formató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. Przedziały pozostawiają w interfejsie puste miejsce, w którym deweloper może wypełniać je według własnego uznania. Na przykład w usługach TopAppBar możesz dostosować te sloty:

Diagram pokazujący dostępne miejsca na pasku aplikacji Material Components

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, navigationIconactions.

Na przykład: Scaffold pozwala na implementację interfejsu z podstawową strukturą układu Material Design. Scaffoldzawiera sloty na najpopularniejsze komponenty Material na najwyższym poziomie, takie jak TopAppBar, BottomAppBar, FloatingActionButtonDrawer. Dzięki użyciu Scaffold możesz łatwo sprawdzić, czy te komponenty są prawidłowo umieszczone i czy współpracują ze sobą.

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

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