Kolejność ograniczeń i modyfikatorów

W Compose możesz łączyć ze sobą wiele modyfikatorów, aby zmieniać wygląd i styl komponentu. Te łańcuchy modyfikatorów mogą wpływać na ograniczenia przekazywane do komponentów kompozycyjnych, które określają granice szerokości i wysokości.

Na tej stronie opisujemy, jak połączone modyfikatory wpływają na ograniczenia, a w konsekwencji na pomiar i umieszczanie komponentów.

Modyfikatory w drzewie interfejsu

Aby zrozumieć, jak modyfikatory wpływają na siebie nawzajem, warto wyobrazić sobie, jak wyglądają w drzewie interfejsu, które jest generowane w fazie kompozycji. Więcej informacji znajdziesz w sekcji Kompozycja.

W drzewie interfejsu możesz wizualizować modyfikatory jako węzły opakowujące węzły układu:

Kod funkcji kompozycyjnych i modyfikatorów oraz ich wizualna reprezentacja w postaci drzewa interfejsu.
Rysunek 1. Modyfikatory otaczające węzły układu w drzewie interfejsu.

Dodanie do komponentu więcej niż jednego modyfikatora tworzy łańcuch modyfikatorów. Gdy połączysz ze sobą kilka modyfikatorów, każdy węzeł modyfikatora zawiera pozostałą część łańcucha i węzeł układu. Na przykład, gdy połączysz modyfikatory clipsize, węzeł modyfikatora clip otoczy węzeł modyfikatora size, który z kolei otoczy węzeł układu Image.

W fazie układu algorytm przechodzący przez drzewo pozostaje taki sam, ale odwiedzany jest też każdy węzeł modyfikatora. W ten sposób modyfikator może zmienić wymagania dotyczące rozmiaru i umiejscowienia modyfikatora lub węzła układu, który obejmuje.

Jak pokazano na rysunku 2, implementacja funkcji kompozycyjnych ImageText składa się z łańcucha modyfikatorów otaczających pojedynczy węzeł układu. Implementacje elementów RowColumn to po prostu węzły układu, które opisują sposób rozmieszczenia elementów podrzędnych.

Drzewo ma taką samą strukturę jak wcześniej, ale każdy węzeł to teraz prosty układ z wieloma węzłami modyfikatorów.
Rysunek 2. Ta sama struktura drzewa co na rysunku 1, ale z komponentami kompozycyjnymi w drzewie interfejsu widocznymi jako łańcuchy modyfikatorów.

Podsumowując:

  • Modyfikatory otaczają pojedynczy modyfikator lub węzeł układu.
  • Węzły układu mogą rozmieszczać wiele węzłów podrzędnych.

W sekcjach poniżej opisujemy, jak używać tego modelu myślowego do wnioskowania o łączeniu modyfikatorów i jak wpływa ono na rozmiar komponentów kompozycyjnych.

Ograniczenia w fazie układu

Faza układu przebiega zgodnie z 3-etapowym algorytmem, który pozwala znaleźć szerokość, wysokość i współrzędne x i y każdego węzła układu:

  1. Mierzenie dzieci: węzeł mierzy swoje węzły podrzędne, jeśli takie istnieją.
  2. Określanie własnego rozmiaru: na podstawie tych pomiarów węzeł określa własny rozmiar.
  3. Umieść elementy podrzędne: każdy węzeł podrzędny jest umieszczany względem własnej pozycji węzła.

Constraints pomagają znaleźć odpowiednie rozmiary węzłów w pierwszych 2 krokach algorytmu. Ograniczenia określają minimalną i maksymalną szerokość i wysokość węzła. Gdy węzeł określi swój rozmiar, jego zmierzony rozmiar powinien mieścić się w tym zakresie.

Rodzaje ograniczeń

Ograniczenie może być jednym z tych typów:

  • Ograniczony: węzeł ma maksymalną i minimalną szerokość oraz wysokość.
Ograniczenia o różnych rozmiarach w kontenerze.
Rysunek 3. Ograniczenia zakresu.
  • Nieograniczony: węzeł nie jest ograniczony żadnym rozmiarem. Maksymalna szerokość i wysokość są ustawione na nieskończoność.
Nieograniczone ograniczenia, w których szerokość i wysokość są ustawione na nieskończoność. Ograniczenia wykraczają poza kontener.
Rysunek 4. nieograniczone ograniczenia.
  • Dokładny: węzeł ma spełnić dokładne wymagania dotyczące rozmiaru. Minimalne i maksymalne granice mają tę samą wartość.
Dokładne ograniczenia, które spełniają dokładne wymagania dotyczące rozmiaru w kontenerze.
Rysunek 5. dokładne ograniczenia.
  • Kombinacja: węzeł podlega kombinacji powyższych typów ograniczeń. Na przykład ograniczenie może określać szerokość, ale pozwalać na nieograniczoną maksymalną wysokość, lub określać dokładną szerokość, ale zapewniać ograniczoną wysokość.
Dwa kontenery, które pokazują kombinacje ograniczonych i nieograniczonych ograniczeń oraz dokładne szerokości i wysokości.
Rysunek 6. kombinacje ograniczonych i nieograniczonych ograniczeń oraz dokładne szerokości i wysokości.

W następnej sekcji opisujemy, jak te ograniczenia są przekazywane z elementu nadrzędnego do elementu podrzędnego.

Jak ograniczenia są przekazywane z elementu nadrzędnego do podrzędnego

W pierwszym kroku algorytmu opisanego w sekcji Ograniczenia w fazie układu ograniczenia są przekazywane z elementu nadrzędnego do podrzędnego w drzewie interfejsu.

Gdy węzeł nadrzędny mierzy swoje węzły podrzędne, przekazuje im te ograniczenia, aby poinformować je, jak duże lub małe mogą być. Następnie, gdy określa swój rozmiar, przestrzega też ograniczeń przekazanych przez jego elementy nadrzędne.

Ogólnie algorytm działa w ten sposób:

  1. Aby określić rozmiar, który ma zajmować, węzeł główny w drzewie interfejsu mierzy swoje elementy podrzędne i przekazuje te same ograniczenia do pierwszego elementu podrzędnego.
  2. Jeśli dziecko jest modyfikatorem, który nie ma wpływu na pomiar, przekazuje ograniczenia do następnego modyfikatora. Ograniczenia są przekazywane w łańcuchu modyfikatorów w niezmienionej postaci, chyba że zostanie osiągnięty modyfikator, który ma wpływ na pomiary. Ograniczenia są następnie odpowiednio zmieniane.
  3. Gdy węzeł nie ma już węzłów podrzędnych (nazywa się go „węzłem liściowym”), określa swój rozmiar na podstawie przekazanych ograniczeń i zwraca ten rozmiar do węzła nadrzędnego.
  4. Element nadrzędny dostosowuje swoje ograniczenia na podstawie pomiarów elementu podrzędnego i wywołuje kolejny element podrzędny z tymi dostosowanymi ograniczeniami.
  5. Gdy wszystkie elementy podrzędne elementu nadrzędnego zostaną zmierzone, węzeł nadrzędny określa swój rozmiar i przekazuje tę informację do swojego elementu nadrzędnego.
  6. Dzięki temu całe drzewo jest przeszukiwane w głąb. W końcu wszystkie węzły ustalą swoje rozmiary i krok pomiaru zostanie zakończony.

Szczegółowy przykład znajdziesz w filmie Constraints and modifier order (Ograniczenia i kolejność modyfikatorów).

Modyfikatory wpływające na ograniczenia

W poprzedniej sekcji dowiedzieliśmy się, że niektóre modyfikatory mogą wpływać na rozmiar ograniczenia. W sekcjach poniżej opisujemy konkretne modyfikatory, które wpływają na ograniczenia.

size modyfikator

Modyfikator size określa preferowany rozmiar treści.

Na przykład ten drzewo interfejsu powinno być renderowane w kontenerze 300dp przez 200dp. Ograniczenia są ograniczone, co pozwala na szerokość od 100dp do 300dp i wysokość od 100dp do 200dp:

Fragment drzewa interfejsu z modyfikatorem rozmiaru otaczającym węzeł układu oraz reprezentacją ograniczonych ograniczeń ustawionych przez modyfikator rozmiaru w kontenerze.
Rysunek 7. Ograniczenia w drzewie interfejsu i ich reprezentacja w kontenerze.

Modyfikator size dostosowuje przychodzące ograniczenia do wartości, która jest do niego przekazywana. W tym przykładzie wartość to 150dp:

To samo co na rysunku 7, z tym że modyfikator rozmiaru dostosowuje przychodzące ograniczenia do wartości przekazanej do niego.
Rysunek 8. Modyfikator size dostosowuje ograniczenia do wartości 150dp.

Jeśli szerokość i wysokość są mniejsze niż najmniejsza wartość ograniczenia lub większe niż największa wartość ograniczenia, modyfikator dopasowuje się do przekazanych ograniczeń w największym możliwym stopniu, zachowując jednocześnie ograniczenia przekazane w tych parametrach:

2 drzewa interfejsu i ich reprezentacje w kontenerach. W pierwszym przypadku modyfikator rozmiaru akceptuje przychodzące ograniczenia, a w drugim dostosowuje się do zbyt dużych ograniczeń w jak największym stopniu, co powoduje, że ograniczenia wypełniają kontener.
Rysunek 9. Modyfikator size, który jak najdokładniej spełnia przekazane ograniczenie.

Pamiętaj, że łączenie kilku modyfikatorów size nie działa. Pierwszy modyfikator size ustawia zarówno minimalne, jak i maksymalne ograniczenia na stałą wartość. Nawet jeśli drugi modyfikator rozmiaru zażąda mniejszego lub większego rozmiaru, musi on nadal mieścić się w dokładnych przekazanych granicach, więc nie zastąpi tych wartości:

Ciąg 2 modyfikatorów rozmiaru w drzewie interfejsu i jego reprezentacja w kontenerze, która jest wynikiem pierwszej przekazanej wartości, a nie drugiej.
Rysunek 10. Ciąg 2 modyfikatorów size, w którym druga przekazana wartość (50dp) nie zastępuje pierwszej wartości (100dp).

requiredSize modyfikator

Jeśli chcesz, aby węzeł zastępował przychodzące ograniczenia, użyj modyfikatora requiredSize zamiast size. Modyfikator requiredSize zastępuje przychodzące ograniczenia i przekazuje określony przez Ciebie rozmiar jako dokładne granice.

Gdy rozmiar zostanie przekazany z powrotem w górę drzewa, węzeł podrzędny zostanie wyśrodkowany w dostępnej przestrzeni:

Modyfikator size i requiredSize połączone w drzewie interfejsu i odpowiednia reprezentacja w kontenerze. Ograniczenia modyfikatora requiredSize zastępują ograniczenia modyfikatora size.
Rysunek 11. Modyfikator requiredSize zastępuje ograniczenia przychodzące z modyfikatora size.

Modyfikatory widthheight

Modyfikator size dostosowuje zarówno szerokość, jak i wysokość ograniczeń. Za pomocą modyfikatorawidth możesz ustawić stałą szerokość, ale pozostawić wysokość nieokreśloną. Podobnie za pomocą modyfikatora height możesz ustawić stałą wysokość, ale pozostawić szerokość nieokreśloną:

Dwa drzewa interfejsu: jedno z modyfikatorem szerokości i jego reprezentacją w kontenerze, a drugie z modyfikatorem wysokości i jego reprezentacją.
Rysunek 12. Modyfikator width i modyfikator height ustawiają odpowiednio stałą szerokość i wysokość.

sizeIn modyfikator

Modyfikator sizeIn umożliwia ustawienie dokładnych ograniczeń minimalnych i maksymalnych szerokości i wysokości. Jeśli potrzebujesz precyzyjnej kontroli nad ograniczeniami, użyj modyfikatora sizeIn.

Drzewo interfejsu z modyfikatorem sizeIn z ustawionymi minimalną i maksymalną szerokością oraz wysokością oraz jego reprezentacja w kontenerze.
Rysunek 13. Modyfikator sizeIn z ustawionymi wartościami minWidth, maxWidth, minHeightmaxHeight.

Przykłady

W tej sekcji znajdziesz dane wyjściowe z kilku fragmentów kodu z połączonymi modyfikatorami oraz ich wyjaśnienie.

Image(
    painterResource(R.drawable.hero),
    contentDescription = null,
    Modifier
        .fillMaxSize()
        .size(50.dp)
)

Ten fragment kodu generuje te dane wyjściowe:

  • Modyfikator fillMaxSize zmienia ograniczenia, aby ustawić zarówno minimalną szerokość, jak i wysokość na wartość maksymalną – 300dp w przypadku szerokości i 200dp w przypadku wysokości.
  • Mimo że modyfikator size chce użyć rozmiaru 50dp, musi on nadal przestrzegać przychodzących ograniczeń minimalnych. Modyfikator size zwróci też dokładne granice ograniczeń 300 na 200, co spowoduje zignorowanie wartości podanej w modyfikatorze size.
  • Wartość Image mieści się w tych granicach i ma rozmiar 300 × 200, który jest przekazywany w górę drzewa.

Image(
    painterResource(R.drawable.hero),
    contentDescription = null,
    Modifier
        .fillMaxSize()
        .wrapContentSize()
        .size(50.dp)
)

Ten fragment kodu generuje te dane wyjściowe:

  • Modyfikator fillMaxSize dostosowuje ograniczenia, aby ustawić zarówno minimalną szerokość, jak i wysokość na wartość maksymalną – 300dp w przypadku szerokości i 200dp w przypadku wysokości.
  • Modyfikator wrapContentSize resetuje minimalne ograniczenia. Dlatego, gdy fillMaxSize powoduje ustalenie stałych ograniczeń, wrapContentSize przywraca ograniczenia do wartości granicznych. Węzeł może teraz ponownie zajmować całą przestrzeń lub być mniejszy od niej.
  • Modyfikator size ustawia ograniczenia na minimalne i maksymalne granice 50.
  • Element Image ma rozmiar 50 × 50, a modyfikator size przekazuje ten rozmiar dalej.
  • Modyfikator wrapContentSize ma specjalną właściwość. Pobiera element podrzędny i umieszcza go na środku dostępnych minimalnych granic, które zostały mu przekazane. Rozmiar, który przekazuje do elementów nadrzędnych, jest więc równy minimalnym granicom, które zostały do niego przekazane.

Łącząc tylko 3 modyfikatory, możesz określić rozmiar elementu kompozycyjnego i wyśrodkować go w elemencie nadrzędnym.

Image(
    painterResource(R.drawable.hero),
    contentDescription = null,
    Modifier
        .clip(CircleShape)
        .padding(10.dp)
        .size(100.dp)
)

Ten fragment kodu generuje te dane wyjściowe:

  • Modyfikator clip nie zmienia ograniczeń.
    • Modyfikator padding obniża maksymalne ograniczenia.
    • Modyfikator size ustawia wszystkie ograniczenia na 100dp.
    • Image spełnia te ograniczenia i podaje rozmiar 100 × 100dp.
    • Modyfikator padding dodaje 10dp do wszystkich rozmiarów, więc zwiększa zgłaszaną szerokość i wysokość o 20dp.
    • W fazie rysowania modyfikator clip działa na obszarze o wymiarach 120 × 120dp. Dlatego tworzy maskę w kształcie koła o tym rozmiarze.
    • Modyfikator padding wstawia wtedy treść o 10dp we wszystkich rozmiarach, więc zmniejsza rozmiar obszaru roboczego do 100 × 100dp.
    • Na tym obszarze roboczym rysowany jest element Image. Obraz jest przycinany na podstawie pierwotnego okręgu 120dp, więc wynik nie jest okrągły.