Modyfikatory tworzenia wiadomości

Modyfikatory umożliwiają dekorowanie lub uzupełnienie elementu kompozycyjnego. Dzięki modyfikatorom możesz:

  • Zmienianie rozmiaru, układu, działania i wyglądu elementu kompozycyjnego
  • dodawać informacje, np. etykiety ułatwień dostępu;
  • Przetwarzaj dane wejściowe użytkownika
  • Dodawaj interakcje wysokiego poziomu, np. umożliwiać klikanie, przewijanie, przeciąganie czy powiększanie.

Modyfikatory to standardowe obiekty Kotlin. Utwórz modyfikator, wywołując jedną z funkcji klasy Modifier:

@Composable
private fun Greeting(name: String) {
    Column(modifier = Modifier.padding(24.dp)) {
        Text(text = "Hello,")
        Text(text = name)
    }
}

Dwa wiersze tekstu na kolorowym tle z dopełnieniem wokół tekstu.

Możesz połączyć te funkcje w łańcuch, aby je utworzyć:

@Composable
private fun Greeting(name: String) {
    Column(
        modifier = Modifier
            .padding(24.dp)
            .fillMaxWidth()
    ) {
        Text(text = "Hello,")
        Text(text = name)
    }
}

Kolorowe tło za tekstem rozciąga się teraz na całą szerokość urządzenia.

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

  • padding umieszcza odstęp wokół elementu.
  • fillMaxWidth sprawia, że funkcja kompozycyjna wypełnia maksymalną szerokość, którą otrzymuje od elementu nadrzędnego.

Sprawdzoną metodą jest, by wszystkie obiekty kompozycyjne akceptują parametr modifier i przekazywać go do pierwszego elementu podrzędnego, który generuje interfejs użytkownika. Dzięki temu kod jest łatwiejszy do wielokrotnego użytku, a jego działanie jest bardziej przewidywalne i intuicyjne. Więcej informacji znajdziesz w wytycznych dotyczących interfejsu Compose API w artykule Elementy akceptują i respektują parametr modyfikatora.

Kolejność modyfikatorów ma znaczenie

Kolejność funkcji modyfikatorów jest istotna. Każda funkcja wprowadza zmiany w wartości Modifier zwróconej przez poprzednią funkcję, więc sekwencja wpływa na wynik końcowy. Spójrzmy na przykład:

@Composable
fun ArtistCard(/*...*/) {
    val padding = 16.dp
    Column(
        Modifier
            .clickable(onClick = onClick)
            .padding(padding)
            .fillMaxWidth()
    ) {
        // rest of the implementation
    }
}

Cały obszar, w tym dopełnienie wokół krawędzi, reaguje na kliknięcia

W kodzie powyżej całego obszaru można kliknąć cały obszar, w tym otaczające go dopełnienie, ponieważ modyfikator padding został zastosowany po modyfikatorze clickable. Jeśli kolejność modyfikatorów jest odwrócona, spacja dodana przez padding nie reaguje na dane wejściowe użytkownika:

@Composable
fun ArtistCard(/*...*/) {
    val padding = 16.dp
    Column(
        Modifier
            .padding(padding)
            .clickable(onClick = onClick)
            .fillMaxWidth()
    ) {
        // rest of the implementation
    }
}

Dopełnienie wokół krawędzi układu nie reaguje już na kliknięcia

Wbudowane modyfikatory

Jetpack Compose zawiera listę wbudowanych modyfikatorów, które pomogą Ci udekorować lub wzbogacić funkcję kompozycyjną. Oto kilka typowych modyfikatorów, których możesz używać do dostosowywania układów.

paddingsize

Domyślnie układy dostępne w sekcji Utwórz zawierają elementy podrzędne. Rozmiar możesz jednak określić, korzystając z modyfikatora size:

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.size(width = 400.dp, height = 100.dp)
    ) {
        Image(/*...*/)
        Column { /*...*/ }
    }
}

Pamiętaj, że podany rozmiar może nie być respektowany, jeśli nie spełnia ograniczeń nałożonych na element nadrzędny układu. Jeśli chcesz, aby rozmiar kompozycyjny był stały bez względu na przychodzące ograniczenia, użyj modyfikatora requiredSize:

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.size(width = 400.dp, height = 100.dp)
    ) {
        Image(
            /*...*/
            modifier = Modifier.requiredSize(150.dp)
        )
        Column { /*...*/ }
    }
}

Obraz podrzędny jest większy niż ograniczenia nałożone na jego element nadrzędny

W tym przykładzie, nawet jeśli element nadrzędny height ma wartość 100.dp, wysokość elementu Image wyniesie 150.dp, ponieważ pierwszeństwo ma modyfikator requiredSize.

Jeśli chcesz, by układ podrzędny wypełniał całą dostępną wysokość dozwoloną przez element nadrzędny, dodaj modyfikator fillMaxHeight (do wyboru są też właściwości fillMaxSize i fillMaxWidth):

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.size(width = 400.dp, height = 100.dp)
    ) {
        Image(
            /*...*/
            modifier = Modifier.fillMaxHeight()
        )
        Column { /*...*/ }
    }
}

Wysokość obrazu jest taka sama jak jego wysokość nadrzędna

Aby dodać dopełnienie dookoła elementu, ustaw modyfikator padding.

Jeśli chcesz dodać dopełnienie powyżej linii bazowej tekstu, aby osiągnąć określoną odległość od górnej krawędzi układu do punktu odniesienia, użyj modyfikatora paddingFromBaseline:

@Composable
fun ArtistCard(artist: Artist) {
    Row(/*...*/) {
        Column {
            Text(
                text = artist.name,
                modifier = Modifier.paddingFromBaseline(top = 50.dp)
            )
            Text(artist.lastSeenOnline)
        }
    }
}

Tekst z dopełnieniem nad nim

Przesunięcie

Aby określić położenie układu względem jego pierwotnej pozycji, dodaj modyfikator offset i ustaw przesunięcie na osi x i y. Kompensacja może być dodatnia lub niedodatnia. Różnica między funkcją padding a offset polega na tym, że dodanie elementu offset do funkcji kompozycyjnej nie zmienia jej pomiarów:

@Composable
fun ArtistCard(artist: Artist) {
    Row(/*...*/) {
        Column {
            Text(artist.name)
            Text(
                text = artist.lastSeenOnline,
                modifier = Modifier.offset(x = 4.dp)
            )
        }
    }
}

Tekst został przeniesiony na prawą stronę swojego kontenera nadrzędnego

Modyfikator offset jest stosowany poziomo zgodnie z kierunkiem układu. W kontekście od lewej do prawej dodatni offset przesuwa element w prawo, a w kontekście od prawej do lewej – w lewo. Jeśli musisz ustawić przesunięcie bez uwzględniania kierunku układu, zapoznaj się z modyfikatorem absoluteOffset, w którym dodatnia wartość przesunięcia zawsze przesuwa element w prawo.

Modyfikator offset udostępnia 2 przeciążenia – offset, które przyjmuje przesunięcia jako parametry, i offset, które pobiera lambda. Szczegółowe informacje o tym, kiedy używać każdego z nich i jak zoptymalizować wydajność, znajdziesz w sekcji Wydajność tworzenia wiadomości – odkładaj czytanie jak najdłużej.

Zakres bezpieczeństwa w oknie tworzenia wiadomości

W interfejsie Compose znajdują się modyfikatory, których można używać tylko w przypadku elementów podrzędnych określonych elementów kompozycyjnych. Funkcja tworzenia wymusza to za pomocą zakresów niestandardowych.

Jeśli na przykład chcesz, aby obiekt podrzędny był większy niż nadrzędny Box, ale nie chcesz zmieniać rozmiaru Box, użyj modyfikatora matchParentSize. Usługa matchParentSize jest dostępna tylko w BoxScope. Dlatego można go używać tylko w przypadku konta podrzędnego należącego do rodzica w Box.

Bezpieczeństwo zakresu uniemożliwia dodawanie modyfikatorów, które nie działają w innych funkcjach kompozycyjnych i zakresach, a także pozwala zaoszczędzić czas na metody prób i błędów.

Modyfikatory o określonym zakresie powiadamiają wydawcę nadrzędnego o pewnych informacjach o elemencie podrzędnym, które powinien mieć. Nazywa się je też nadrzędnymi modyfikatorami danych. Ich elementy wewnętrzne różnią się od zwykłych modyfikatorów, ale z perspektywy użytkowania różnice te nie mają znaczenia.

matchParentSize w: Box

Jak już wspomnieliśmy, jeśli chcesz, by układ podrzędny miał taki sam rozmiar jak nadrzędny Box, ale nie wpływał na rozmiar Box, użyj modyfikatora matchParentSize.

Pamiętaj, że właściwość matchParentSize jest dostępna tylko w zakresie Box, co oznacza, że ma zastosowanie tylko do bezpośrednich elementów podrzędnych funkcji kompozycyjnych Box.

W poniższym przykładzie element podrzędny Spacer pobiera swój rozmiar z elementu nadrzędnego (Box), który z kolei pobiera swój rozmiar od największych elementów podrzędnych – w tym przypadku ArtistCard.

@Composable
fun MatchParentSizeComposable() {
    Box {
        Spacer(
            Modifier
                .matchParentSize()
                .background(Color.LightGray)
        )
        ArtistCard()
    }
}

Szare tło wypełniające kontener

Gdyby zamiast matchParentSize użyto elementu fillMaxSize, Spacer zajęłoby całe dostępne miejsce dostępne dla jednostki nadrzędnej, a tym samym spowodowałoby jej rozwinięcie i wypełnienie całej dostępnej przestrzeni.

Szare tło wypełnia ekran

weight w: Row i Column

Jak wiesz już w poprzedniej sekcji dotyczącej Dopełniania i rozmiaru, domyślnie rozmiar kompozycyjny jest definiowany przez zawijanie treści. Rozmiar elementu kompozycyjnego można ustawić tak, aby był elastyczny w obrębie jego nadrzędnego, modyfikatorowi weight, który jest dostępny tylko w interfejsach RowScope i ColumnScope.

Weźmy przykład Row, który zawiera 2 kompozycje Box. Pierwsze pole jest 2 razy większe niż weight drugiego, więc ma 2 razy większą szerokość. Row ma szerokość 210.dp, więc pierwszy wymiar (Box) ma 140.dp, a drugi – 70.dp:

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.fillMaxWidth()
    ) {
        Image(
            /*...*/
            modifier = Modifier.weight(2f)
        )
        Column(
            modifier = Modifier.weight(1f)
        ) {
            /*...*/
        }
    }
}

Szerokość obrazu jest 2-krotnie większa niż szerokość tekstu

Wyodrębnianie i ponowne wykorzystywanie modyfikatorów

Różne modyfikatory można łączyć, aby dekorować lub wzbogacać kompozycję. Ten łańcuch jest tworzony za pomocą interfejsu Modifier, który reprezentuje uporządkowaną, stałą listę pojedynczych elementów Modifier.Elements.

Każdy element Modifier.Element reprezentuje konkretne zachowanie, takie jak działanie układu, rysowania i grafiki, wszystkie zachowania związane z gestami, skupieniem i semantyką, a także zdarzenia wejściowe urządzenia. Ich kolejność ma znaczenie: elementy modyfikujące, które zostały dodane jako pierwsze, będą stosowane jako pierwsze.

Czasami warto używać tych samych instancji łańcucha modyfikatora w wielu elementach kompozycyjnych przez wyodrębnienie ich do zmiennych i przeniesienie ich na wyższe zakresy. Może zwiększyć czytelność kodu lub poprawić wydajność aplikacji z kilku powodów:

  • Zmiana przydziału modyfikatorów nie będzie powtarzana po wykonaniu tej zmiany w elementach kompozycyjnych, które ich używają
  • Łańcuchy modyfikatorów mogą być potencjalnie bardzo długie i złożone, dlatego ponowne użycie tego samego wystąpienia łańcucha może złagodzić potrzeby środowiska wykonawczego tworzenia zadań przy ich porównywaniu.
  • To wyodrębnianie promuje czystość, spójność i utrzymanie kodu w bazie kodu

Sprawdzone metody ponownego wykorzystywania modyfikatorów

Utwórz własne łańcuchy typu Modifier i wyodrębnij je, aby wykorzystać je w wielu komponentach kompozycyjnych. Nie ma potrzeby zapisywania modyfikatora, ponieważ są to obiekty podobne do danych:

val reusableModifier = Modifier
    .fillMaxWidth()
    .background(Color.Red)
    .padding(12.dp)

Wyodrębnianie i ponowne wykorzystywanie modyfikatorów podczas obserwacji często zmieniających się stanów

Podczas obserwacji często zmieniających się stanów w komponentach kompozycyjnych, takich jak stany animacji czy scrollState, może dojść do sporo zmian. W takim przypadku modyfikatory zostaną przydzielone przy każdej zmianie kompozycji i potencjalnie w każdej klatce:

@Composable
fun LoadingWheelAnimation() {
    val animatedState = animateFloatAsState(/*...*/)

    LoadingWheel(
        // Creation and allocation of this modifier will happen on every frame of the animation!
        modifier = Modifier
            .padding(12.dp)
            .background(Color.Gray),
        animatedState = animatedState
    )
}

Zamiast tego możesz utworzyć, wyodrębnić i ponownie wykorzystać to samo wystąpienie modyfikatora oraz przekazać je do funkcji kompozycyjnej w ten sposób:

// Now, the allocation of the modifier happens here:
val reusableModifier = Modifier
    .padding(12.dp)
    .background(Color.Gray)

@Composable
fun LoadingWheelAnimation() {
    val animatedState = animateFloatAsState(/*...*/)

    LoadingWheel(
        // No allocation, as we're just reusing the same instance
        modifier = reusableModifier,
        animatedState = animatedState
    )
}

Wyodrębnianie i ponowne wykorzystywanie modyfikatorów bez zakresu

Modyfikatory mogą być nieograniczone lub ograniczone do konkretnego elementu kompozycyjnego. W przypadku modyfikatorów o nieograniczonym zakresie można je łatwo wyodrębnić poza obiektami kompozycyjnymi jako proste zmienne:

val reusableModifier = Modifier
    .fillMaxWidth()
    .background(Color.Red)
    .padding(12.dp)

@Composable
fun AuthorField() {
    HeaderText(
        // ...
        modifier = reusableModifier
    )
    SubtitleText(
        // ...
        modifier = reusableModifier
    )
}

Jest to szczególnie przydatne w połączeniu z leniwymi układami. W większości przypadków wszystkie potencjalnie istotne elementy mają mieć dokładnie takie same modyfikatory:

val reusableItemModifier = Modifier
    .padding(bottom = 12.dp)
    .size(216.dp)
    .clip(CircleShape)

@Composable
private fun AuthorList(authors: List<Author>) {
    LazyColumn {
        items(authors) {
            AsyncImage(
                // ...
                modifier = reusableItemModifier,
            )
        }
    }
}

Wyodrębnianie i ponowne wykorzystywanie modyfikatorów zakresu

Gdy zajmujesz się modyfikatorami, które są ograniczone do określonych elementów kompozycyjnych, możesz wyodrębnić je na najwyższym możliwym poziomie i ponownie je wykorzystać w razie potrzeby:

Column(/*...*/) {
    val reusableItemModifier = Modifier
        .padding(bottom = 12.dp)
        // Align Modifier.Element requires a ColumnScope
        .align(Alignment.CenterHorizontally)
        .weight(1f)
    Text1(
        modifier = reusableItemModifier,
        // ...
    )
    Text2(
        modifier = reusableItemModifier
        // ...
    )
    // ...
}

Modyfikatory wyodrębnione z zakresu należy przekazywać do elementów podrzędnych o tym samym zakresie i zakresie. Więcej informacji o tym, dlaczego jest to tak ważne, znajdziesz w sekcji Bezpieczeństwo w oknie tworzenia:

Column(modifier = Modifier.fillMaxWidth()) {
    // Weight modifier is scoped to the Column composable
    val reusableItemModifier = Modifier.weight(1f)

    // Weight will be properly assigned here since this Text is a direct child of Column
    Text1(
        modifier = reusableItemModifier
        // ...
    )

    Box {
        Text2(
            // Weight won't do anything here since the Text composable is not a direct child of Column
            modifier = reusableItemModifier
            // ...
        )
    }
}

Dalsze łańcuch wyodrębnionych modyfikatorów

Aby dodać łańcuch lub dołączyć wyodrębnione łańcuchy modyfikatorów, wywołaj funkcję .then():

val reusableModifier = Modifier
    .fillMaxWidth()
    .background(Color.Red)
    .padding(12.dp)

// Append to your reusableModifier
reusableModifier.clickable { /*...*/ }

// Append your reusableModifier
otherModifier.then(reusableModifier)

Pamiętaj tylko, że kolejność modyfikatorów ma znaczenie.

Więcej informacji

Podajemy pełną listę modyfikatorów z ich parametrami i zakresami.

Aby dowiedzieć się więcej o korzystaniu z modyfikatorów, możesz też zapoznać się z podstawowymi układami w ćwiczeniach z programowania w narzędziu do tworzenia lub zapoznać się z repozytorium Now w Androidzie.

Więcej informacji o modyfikatorach niestandardowych i sposobie ich tworzenia znajdziesz w dokumentacji w artykule Układy niestandardowe – korzystanie z modyfikatora układu.