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 interfejsie za pomocą kompozycji, układu i rysowania

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:

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 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 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")
    }
}

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

Podobnie możesz użyć Row, aby umieścić elementy na ekranie w poziomie. 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

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")
    }
}

Dwa elementy ułożone jeden na drugim

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.

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

Aby ustawić pozycję elementów potomnych w elementach Row, użyj argumentów horizontalArrangementverticalAlignment. 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 przejściu. 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:

  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ściem, więc podaje rozmiar i zwraca instrukcje dotyczące umieszczenia.
  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ł Row zmierzono, ustawiono jego rozmiar i umieszczono jego elementy potomne, może on określić swój rozmiar i położenie.

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

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 układu, wszelkie zmiany, które wpływają tylko na rozmieszczenie 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),
        ) { /*...*/ }
    }
}

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 podczas tworzenia 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:

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 ->
            // ...
        }
    }
}