Modyfikatory tworzenia wiadomości

Modyfikatory umożliwiają dekorowanie lub rozszerzanie funkcji kompozycyjnej. Modyfikatory umożliwiają:

  • zmieniać rozmiar, układ, zachowanie i wygląd elementu kompozycyjnego;
  • dodawać informacje, takie jak etykiety ułatwień dostępu;
  • Przetwarzanie danych wejściowych użytkownika
  • dodawać interakcje wysokiego poziomu, takie jak możliwość kliknięcia, przewijania, przeciągania lub powiększania elementu;

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, aby je skomponować:

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

Kolorowe tło za tekstem zajmuje teraz całą szerokość urządzenia.

W kodzie powyżej zwróć uwagę na różne funkcje modyfikatora używane razem.

  • padding dodaje odstęp wokół elementu.
  • fillMaxWidth sprawia, że element kompozycyjny wypełnia maksymalną szerokość nadaną mu przez element nadrzędny.

Sprawdzoną metodą jest, aby wszystkie funkcje kompozycyjne akceptowały parametr modifier i przekazywały ten modyfikator do pierwszego elementu podrzędnego, który emituje interfejs. Dzięki temu kod będzie bardziej uniwersalny, a jego działanie – bardziej przewidywalne i intuicyjne. Więcej informacji znajdziesz w wytycznych dotyczących interfejsu Compose API, Elementy akceptują i respektują parametr Modifier.

Kolejność modyfikatorów ma znaczenie

Kolejność funkcji modyfikatora jest istotna. Każda funkcja wprowadza zmiany w Modifier zwróconym przez poprzednią funkcję, więc kolejność wpływa na ostateczny wynik. Oto 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 powyższym kodzie cały obszar jest klikalny, w tym otaczające go dopełnienie, ponieważ modyfikator padding został zastosowany po modyfikatorze clickable. Jeśli kolejność modyfikatorów zostanie odwrócona, miejsce dodane przez padding nie będzie reagować 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 udostępnia listę wbudowanych modyfikatorów, które pomagają dekorować lub rozszerzać funkcje kompozycyjne. Oto kilka typowych modyfikatorów, których będziesz używać do dostosowywania układów.

padding i size

Domyślnie układy udostępniane w Compose zawijają elementy podrzędne. Możesz jednak ustawić rozmiar za pomocą modyfikatora size:

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

Pamiętaj, że określony przez Ciebie rozmiar może nie zostać uwzględniony, jeśli nie spełnia ograniczeń wynikających z elementu nadrzędnego układu. Jeśli chcesz, aby rozmiar funkcji kompozycyjnej był stały niezależnie od ograniczeń przychodzących, 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 pochodzące z obrazu nadrzędnego

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

Jeśli chcesz, aby układ podrzędny wypełniał całą dostępną wysokość dozwoloną przez układ nadrzędny, dodaj modyfikator fillMaxHeight (Compose udostępnia też 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 elementu nadrzędnego.

Aby dodać dopełnienie wokół elementu, ustaw modyfikator padding. Parametry start i end elementu padding są zgodne z kierunkiem układu. Aby dodać dopełnienie do lewej lub prawej krawędzi niezależnie od kierunku układu, użyj absolutePadding z parametrami left i right.

Jeśli chcesz dodać dopełnienie nad linią bazową tekstu, aby uzyskać określoną odległość od góry układu do linii bazowej, 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 ustawić układ względem jego pierwotnej pozycji, dodaj modyfikator offset i ustaw przesunięcie na osiach x i y. Przesunięcia mogą być dodatnie lub niedodatnie. Różnica między padding a offset polega na tym, że dodanie offset do funkcji kompozycyjnej nie zmienia jej wymiarów:

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

Tekst przesunięty na prawą stronę kontenera nadrzędnego

Modyfikator offset jest stosowany w poziomie zgodnie z kierunkiem układu. W kontekście od lewej do prawej dodatnia wartość offset przesuwa element w prawo, a w kontekście od prawej do lewej – w lewo. Jeśli chcesz 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óry przyjmuje przesunięcia jako parametry, oraz offset, który przyjmuje wyrażenie lambda. Więcej informacji o tym, kiedy używać poszczególnych opcji i jak optymalizować wydajność, znajdziesz w sekcji Wydajność Compose – odraczaj odczyty tak długo, jak to możliwe.

Bezpieczeństwo zakresu w Compose

W Compose są modyfikatory, których można używać tylko wtedy, gdy są stosowane do elementów podrzędnych określonych funkcji kompozycyjnych. Compose wymusza to za pomocą zakresów niestandardowych.

Jeśli na przykład chcesz, aby element podrzędny Box był tak duży jak element nadrzędny, bez wpływu na rozmiar Box, użyj modyfikatora matchParentSize. Funkcja matchParentSize jest dostępna tylko w: BoxScope. Dlatego można go używać tylko w przypadku elementu podrzędnego w elemencie nadrzędnym Box.

Bezpieczeństwo zakresu zapobiega dodawaniu modyfikatorów, które nie działałyby w innych funkcjach kompozycyjnych i zakresach, i oszczędza czas, który trzeba by poświęcić na próby i błędy.

Modyfikatory zakresu powiadamiają element nadrzędny o informacjach, które powinien znać o elemencie podrzędnym. Są one też często nazywane modyfikatorami danych nadrzędnych. Ich wewnętrzne działanie różni się od modyfikatorów ogólnego przeznaczenia, ale z perspektywy użytkowania te różnice nie mają znaczenia.

matchParentSize w: Box

Jak wspomnieliśmy powyżej, jeśli chcesz, aby układ podrzędny miał taki sam rozmiar jak układ nadrzędny Box bez wpływu na rozmiar Box, użyj modyfikatora matchParentSize.

Pamiętaj, że matchParentSize jest dostępny tylko w zakresie Box, co oznacza, że dotyczy tylko bezpośrednich elementów podrzędnych funkcji kompozycyjnych Box.

W przykładzie poniżej element podrzędny Spacer przyjmuje rozmiar elementu nadrzędnego Box, który z kolei przyjmuje rozmiar największego elementu podrzędnego, czyli w tym przypadku ArtistCard.

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

Szare tło wypełniające kontener

Jeśli zamiast matchParentSize użyto by fillMaxSize, Spacer zajmowałby całą dostępną przestrzeń dozwoloną dla elementu nadrzędnego, co z kolei spowodowałoby, że element nadrzędny rozszerzyłby się i wypełnił całą dostępną przestrzeń.

Szare tło wypełniające ekran

weight w usługach Row i Column

Jak pokazaliśmy w poprzedniej sekcji dotyczącej dopełnienia i rozmiaru, domyślnie rozmiar funkcji kompozycyjnej jest określany przez zawartość, którą ona otacza. Możesz ustawić elastyczny rozmiar elementu kompozycyjnego w elemencie nadrzędnym, używając modyfikatora weight, który jest dostępny tylko w RowScope i ColumnScope.

Weźmy element Row, który zawiera 2 komponenty Box. Pierwsze pole ma 2 razy większą wartość weight niż drugie, więc jest 2 razy szersze. Ponieważ Row ma szerokość 210.dp, pierwszy Box ma szerokość 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 razy większa niż szerokość tekstu

Wyodrębnianie i ponowne używanie modyfikatorów

Można połączyć ze sobą wiele modyfikatorów, aby udekorować lub rozszerzyć funkcję kompozycyjną. Ten łańcuch jest tworzony za pomocą interfejsu Modifier, który reprezentuje uporządkowaną, niezmienną listę pojedynczych Modifier.Elements.

Każdy element Modifier.Element reprezentuje pojedyncze zachowanie, takie jak układ, rysowanie i grafika, wszystkie zachowania związane z gestami, zaznaczeniem i semantyką, a także zdarzenia wejściowe urządzenia. Kolejność ma znaczenie: elementy modyfikatora, które zostaną dodane jako pierwsze, zostaną zastosowane jako pierwsze.

Czasami warto ponownie użyć tych samych instancji łańcucha modyfikatorów w wielu elementach kompozycyjnych, wyodrębniając je do zmiennych i przenosząc do wyższych zakresów. Może to poprawić czytelność kodu lub wydajność aplikacji z kilku powodów:

  • Ponowne przydzielanie modyfikatorów nie będzie powtarzane, gdy nastąpi rekompozycja funkcji kompozycyjnych, które ich używają.
  • Łańcuchy modyfikatorów mogą być bardzo długie i skomplikowane, więc ponowne użycie tej samej instancji łańcucha może zmniejszyć obciążenie środowiska wykonawczego Compose podczas ich porównywania.
  • Takie wyodrębnienie sprzyja czystości, spójności i łatwości utrzymania kodu w całej bazie kodu.

Sprawdzone metody ponownego używania modyfikatorów

Twórz własne łańcuchy Modifier i wyodrębniaj je, aby używać ich w wielu komponentach kompozycyjnych. Możesz po prostu zapisać modyfikator, ponieważ są to obiekty podobne do danych:

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

Wyodrębnianie i ponowne używanie modyfikatorów podczas obserwowania często zmieniającego się stanu

Podczas obserwowania często zmieniających się stanów w komponentach, takich jak stany animacji lub scrollState, może wystąpić znaczna liczba rekompozycji. W takim przypadku modyfikatory będą przydzielane przy każdej rekompozycji i potencjalnie dla każdej klatki:

@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 użyć tej samej instancji modyfikatora i przekazać ją 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 używanie modyfikatorów bez zakresu

Modyfikatory mogą być nieograniczone lub ograniczone do konkretnego elementu kompozycyjnego. W przypadku modyfikatorów bez zakresu możesz je łatwo wyodrębnić poza funkcje kompozycyjne jako proste zmienne:

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

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

Może to być szczególnie przydatne w połączeniu z układami Lazy. W większości przypadków chcesz, aby wszystkie Twoje produkty (których może być bardzo dużo) miały 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 używanie modyfikatorów o ograniczonym zakresie

Jeśli masz do czynienia z modyfikatorami, które są ograniczone do określonych funkcji kompozycyjnych, możesz je wyodrębnić na najwyższy możliwy poziom i używać ich ponownie tam, gdzie jest to odpowiednie:

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

Wyodrębnione modyfikatory o określonym zakresie należy przekazywać tylko do bezpośrednich elementów podrzędnych o tym samym zakresie. Więcej informacji o tym, dlaczego jest to ważne, znajdziesz w sekcji Bezpieczeństwo zakresu w Compose:

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 łączenie wyodrębnionych modyfikatorów

Możesz dalej łączyć lub dołączać wyodrębnione łańcuchy modyfikatorów, wywołując 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, że kolejność modyfikatorów ma znaczenie.

Więcej informacji

Udostępniamy pełną listę modyfikatorów wraz z ich parametrami i zakresami.

Aby dowiedzieć się więcej o korzystaniu z modyfikatorów, możesz też zapoznać się z ćwiczeniem z programowania Podstawowe układy w Compose lub repozytorium Now in Android.

Więcej informacji o modyfikatorach niestandardowych i ich tworzeniu znajdziesz w dokumentacji dotyczącej niestandardowych układów i używania modyfikatora układu.