Modyfikatory tworzenia wiadomości

Modyfikatory umożliwiają dekorowanie lub rozszerzanie funkcji kompozycyjnych. Modyfikatory umożliwiają wykonywanie takich czynności:

  • zmieniać rozmiar, układ, działanie i wygląd funkcji kompozycyjnej;
  • Dodawanie informacji, np. etykiet ułatwień dostępu
  • Przetwarzanie danych wejściowych użytkownika
  • Dodawanie interakcji wyższego poziomu, takich 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)
    }
}

2 wiersze tekstu na kolorowym tle z marginesami wokół tekstu.

Możesz łączyć te funkcje, aby je komponować:

@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 powyższym kodzie zwróć uwagę na różne funkcje modyfikujące użyte razem.

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

Zgodnie ze sprawdzonymi metodami wszystkie funkcje kompozycyjne powinny akceptować parametr modifier i przekazywać go do pierwszego elementu podrzędnego, który emituje interfejs. Dzięki temu kod jest bardziej wielokrotnego użytku, a jego działanie staje się bardziej przewidywalne i intuicyjne. Więcej informacji znajdziesz w wytycznych dotyczących interfejsu Compose API, w sekcji Elementy akceptują i respektują parametr modyfikatora.

Kolejność modyfikatorów ma znaczenie

Kolejność funkcji modyfikujących ma znaczenie. Każda funkcja wprowadza zmiany w wartości Modifier zwróconej przez poprzednią funkcję, więc kolejność ma wpływ na wynik końcowy. 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 marginesy wokół krawędzi, reaguje na kliknięcia.

W powyższym kodzie klikalny jest cały obszar, w tym otaczający go margines wewnętrzny, ponieważ modyfikator padding został zastosowany po modyfikatorze clickable. Jeśli kolejność modyfikatorów zostanie odwrócona, spacja dodana 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
    }
}

Margines 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ć funkcjonalność komponentu. Oto kilka typowych modyfikatorów, których będziesz używać do dostosowywania układów.

paddingsize

Domyślnie kompozycje w Compose otaczają swoje 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 rozmiar może nie zostać uwzględniony, jeśli nie spełnia ograniczeń pochodzących z elementu nadrzędnego układu. Jeśli chcesz, aby rozmiar elementu kompozycyjnego 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 elementu nadrzędnego

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

.

Jeśli chcesz, aby układ podrzędny wypełniał całą dostępną wysokość dozwoloną przez element nadrzędny, dodaj modyfikator fillMaxHeight (Compose udostępnia też fillMaxSizefillMaxWidth):

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

Wysokość obrazu jest taka sama jak wysokość elementu nadrzędnego

Aby dodać dopełnienie wokół elementu, ustaw modyfikator padding.

Jeśli chcesz dodać do tekstu dopełnienie powyżej linii bazowej, 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 wypełnieniem nad nim

Przesunięcie

Aby umieścić układ względem jego pierwotnej pozycji, dodaj modyfikator offset i ustaw przesunięcie na osiach xy. Przesunięcia mogą być dodatnie lub niedodatnie. Różnica między paddingoffset polega na tym, że dodanie offset do komponentu nie zmienia jego 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 przesuwa go 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óre przyjmuje przesunięcia jako parametry, i offset, które przyjmuje wyrażenie lambda. Więcej informacji o tym, kiedy używać poszczególnych funkcji i jak optymalizować skuteczność, znajdziesz w sekcji Skuteczność kompozycji – 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 w przypadku elementów podrzędnych określonych komponentów. Compose wymusza to za pomocą zakresów niestandardowych.

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

Bezpieczeństwo zakresu zapobiega dodawaniu modyfikatorów, które nie działają w innych funkcjach kompozycyjnych i zakresach, co pozwala zaoszczędzić czas na próby i błędy.

Modyfikatory z zakresem informują rodzica o pewnych kwestiach, które powinien wiedzieć o dziecku. Są one też często nazywane modyfikatorami danych nadrzędnych. Ich wewnętrzna struktura 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, ale nie wpływał na rozmiar układu nadrzędnego, użyj modyfikatora matchParentSize.BoxBox

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

W przykładzie poniżej element podrzędny Spacer przejmuje rozmiar od elementu nadrzędnego Box, który z kolei przejmuje 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 fillMaxSize, element Spacer zająłby całą dostępną przestrzeń dozwoloną dla elementu nadrzędnego, co spowodowałoby rozszerzenie elementu nadrzędnego i wypełnienie całej dostępnej przestrzeni.

Szare tło wypełniające ekran

weight w Row i Column

Jak widać w poprzedniej sekcji dotyczącej wypełnienia i rozmiaru, domyślnie rozmiar komponentu jest określany przez zawartość, którą otacza. Rozmiar funkcji kompozycyjnej możesz ustawić tak, aby był elastyczny w ramach elementu nadrzędnego, używając weightmodyfikatora dostępnego tylko w RowScopeColumnScope.

Weźmy Row, który zawiera 2 funkcje kompozycyjne Box. Pierwsze pole ma 2 razy większą wartość weight niż drugie, więc ma 2 razy większą szerokość. Ponieważ Row ma szerokość 210.dp, pierwsza Box ma szerokość 140.dp, a druga 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 wykorzystywanie modyfikatorów

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

Każdy symbol Modifier.Element reprezentuje indywidualne zachowanie, np. zachowania związane z układem, rysowaniem i grafiką, wszystkie zachowania związane z gestami, zachowania związane z ogniskiem i semantyką, a także zdarzenia wejścia na urządzeniu. Kolejność ma znaczenie: elementy modyfikujące dodane jako pierwsze zostaną zastosowane jako pierwsze.

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

  • Ponowne przypisanie modyfikatorów nie będzie powtarzane, gdy nastąpi ponowna kompozycja w przypadku funkcji kompozycyjnych, które ich używają.
  • Łańcuchy modyfikatorów mogą być bardzo długie i złożone, więc ponowne użycie tej samej instancji łańcucha może zmniejszyć obciążenie środowiska wykonawczego Compose podczas porównywania ich.
  • Wyodrębnianie kodu zwiększa jego przejrzystość, spójność i łatwość utrzymania w całej bazie kodu.

Sprawdzone metody ponownego wykorzystywania modyfikatorów

Twórz własne Modifier i wyodrębniaj je, aby używać ich w wielu komponentach kompozycyjnych. Możesz po prostu zapisać modyfikator, ponieważ jest on obiektem podobnym do danych:

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

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

Obserwowanie często zmieniających się stanów w funkcjach kompozycyjnych, takich jak stany animacji lub scrollState, może powodować znaczną liczbę ponownych kompozycji. W takim przypadku modyfikatory będą przypisywane przy każdej rekompozycji i potencjalnie przy 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 użyć tę samą instancję 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 wykorzystywanie modyfikatorów bez zakresu

Modyfikatory mogą być nieograniczone lub ograniczone do konkretnego komponentu. 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 korzystne w połączeniu z układami leniwymi. W większości przypadków chcesz, aby wszystkie Twoje produkty, których może być znaczna liczba, miały dokładnie te 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 o określonym zakresie

W przypadku modyfikatorów, które są ograniczone do określonych funkcji kompozycyjnych, możesz wyodrębnić je na najwyższy możliwy poziom i ponownie wykorzystać w odpowiednich miejscach:

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 połączyć lub dołączyć wyodrębnione ciągi 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.

Więcej informacji o używaniu modyfikatorów znajdziesz w ćwiczeniu z programowania dotyczącym podstawowych układów w Compose lub w repozytorium Now in Android.

Więcej informacji o modyfikatorach niestandardowych i sposobie ich tworzenia znajdziesz w dokumentacji Układy niestandardowe – używanie modyfikatora układu.