Kolejność ograniczeń i modyfikatorów

W Compose możesz łączyć ze sobą wiele modyfikatorów, aby zmieniać wygląd i styl funkcji kompozycyjnych. Te łańcuchy modyfikatorów mogą wpływać na ograniczenia przekazywane do funkcji 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 funkcji kompozycyjnych.

Modyfikatory w drzewie iUI

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

W drzewie UI 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 UI.
Rysunek 1. Modyfikatory otaczające węzły układu w drzewie UI.

Dodanie do funkcji kompozycyjnej więcej niż 1 modyfikatora tworzy łańcuch modyfikatorów. Gdy połączysz ze sobą kilka modyfikatorów, każdy węzeł modyfikatora otacza resztę łańcucha i węzeł układu. Jeśli na przykład połączysz modyfikatory clip i size, węzeł modyfikatora clip otoczy węzeł modyfikatora size, który z kolei otoczy węzeł układu Image.

W fazie układu algorytm, który przechodzi przez drzewo pozostaje taki sam, ale odwiedzany jest też każdy węzeł modyfikatora. Dzięki temu modyfikator może zmieniać wymagania dotyczące rozmiaru i umieszczania modyfikatora lub węzła układu, który otacza.

Jak pokazano na rysunku 2, implementacja samych funkcji kompozycyjnych Image i Text składa się z łańcucha modyfikatorów otaczających pojedynczy węzeł układu.

Implementacje Row i Column to węzły układu, które opisują sposób rozmieszczania elementów podrzędnych.

Struktura drzewa z poprzedniego przykładu, ale teraz każdy węzeł to tylko prosty układ z wieloma węzłami modyfikatorów.
Rysunek 2. Ta sama struktura drzewa co na rysunku 1, ale z funkcjami kompozycyjnymi w drzewie UI wizualizowanymi 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 kolejnych sekcjach opisujemy, jak używać tego modelu mentalnego do rozumowania na temat łączenia modyfikatorów i jego wpływu na rozmiar funkcji kompozycyjnych.

Ograniczenia w fazie układu

Faza układu obejmuje algorytm 3-etapowy, który pozwala znaleźć szerokość, wysokość oraz współrzędne x i y każdego węzła układu:

  1. Mierzenie elementów podrzędnych: 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 swój rozmiar.
  3. Umieszczanie elementów podrzędnych: 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 2 pierwszych krokach algorytmu. Ograniczenia określają minimalne i maksymalne granice szerokości i wysokości węzła. Gdy węzeł określa swój rozmiar, jego zmierzony rozmiar powinien mieścić się w tym zakresie.

Typy ograniczeń

Ograniczenie może należeć do jednej z tych kategorii:

  • Określone: węzeł ma maksymalną i minimalną szerokość oraz wysokość.
Ograniczenia określone o różnych rozmiarach w kontenerze.
Rysunek 3. Ograniczenia określone.
  • Nieokreślone: węzeł nie jest ograniczony żadnym rozmiarem. Maksymalna szerokość i wysokość są ustawione na nieskończoność.
Nieokreślone ograniczenia, w których szerokość i wysokość są ustawione na nieskończoność. Ograniczenia wykraczają poza kontener.
Rysunek 4. Nieokreślone ograniczenia.
  • Dokładne: 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. Oraniczenia dokładne.
  • Kombinacja: węzeł stosuje kombinację poprzednich typów ograniczeń. Na przykład ograniczenie może ograniczać szerokość, ale pozwalać na nieograniczoną maksymalną wysokość, lub ustawiać dokładną szerokość, ale zapewniać ograniczoną wysokość.
Dwa kontenery, które pokazują kombinacje określonych i nieokreślonych ograniczeń oraz dokładne szerokości i wysokości.
Rysunek 6. Kombinacje określonych i nieokreślonych ograniczeń oraz dokładne szerokości i wysokości.

W następnej sekcji opisujemy, jak te ograniczenia są przekazywane z elementu nadrzędnego do 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 UI.

Gdy węzeł nadrzędny mierzy swoje elementy podrzędne, przekazuje te ograniczenia do każdego z nich, aby poinformować je, jak duże lub małe mogą być. Następnie, gdy określa swój rozmiar, stosuje się też do ograniczeń przekazanych przez własne 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 UI mierzy swoje elementy podrzędne i przekazuje te same ograniczenia do pierwszego elementu podrzędnego.
  2. Jeśli element podrzędny 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 zostanie osiągnięty węzeł, który nie ma elementów podrzędnych (nazywany „węzłem-liściem”), określa on swój rozmiar na podstawie przekazanych ograniczeń i zwraca ten rozwiązany rozmiar do elementu nadrzędnego.
  4. Element nadrzędny dostosowuje swoje ograniczenia na podstawie pomiarów tego elementu podrzędnego i wywołuje następny element podrzędny z tymi dostosowanymi ograniczeniami.
  5. Gdy wszystkie elementy podrzędne elementu nadrzędnego zostaną zmierzone, element nadrzędny określa swój rozmiar i przekazuje go do własnego elementu nadrzędnego.
  6. W ten sposób całe drzewo jest przechodzone w głąb. W końcu wszystkie węzły określają swoje rozmiary i etap pomiaru zostaje zakończony.

Szczegółowy przykład znajdziesz w filmie Ograniczenia i kolejność modyfikatorów video.

Modyfikatory, które wpływają na ograniczenia

W poprzedniej sekcji dowiedzieliśmy się, że niektóre modyfikatory mogą wpływać na rozmiar ograniczeń. W kolejnych sekcjach opisujemy konkretne modyfikatory, które mają wpływ na ograniczenia.

Modyfikator size

Modyfikator size deklaruje preferowany rozmiar treści.

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

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

Modyfikator size dostosowuje przychodzące ograniczenia do wartości przekazanej do niego. W tym przykładzie wartość to 150dp:

Podobnie jak na rysunku 7, z tym że modyfikator size dostosowuje przychodzące ograniczenia do wartości przekazanej do niego.
Rysunek 8. Modyfikator size dostosowujący ograniczenia do 150dp.

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

2 drzewa UI 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 stosujący się do przekazanego ograniczenia w jak największym stopniu.

Pamiętaj, że łączenie ze sobą 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, nadal musi stosować się do przekazanych dokładnych granic, więc nie zastąpi tych wartości:

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

Modyfikator requiredSize

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ępnym miejscu:

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

Modyfikatory width i height

Modyfikator size dostosowuje zarówno szerokość, jak i wysokość ograniczeń. Za pomocą modyfikatora width 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 UI: jedno z modyfikatorem szerokości i jego reprezentacją w kontenerze, a drugie z modyfikatorem wysokości i jego reprezentacją.
Rysunek 12. Modyfikatory width i height ustawiające odpowiednio stałą szerokość i wysokość.

Modyfikator sizeIn

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

Drzewo UI 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, minHeight i maxHeight.

Przykłady

W tej sekcji pokazujemy i wyjaśniamy dane wyjściowe z kilku fragmentów kodu z połączonymi modyfikatorami.

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

Ten fragment kodu generuje te dane wyjściowe:

Niebieski kwadrat wypełniający kontener nadrzędny.
Rysunek 14. W wyniku łańcucha modyfikatorów element Image wypełnia maksymalny rozmiar jako rezultat łańcucha modyfikatorów.
  • Modyfikator fillMaxSize zmienia ograniczenia, aby ustawić zarówno minimalną szerokość, jak i wysokość na maksymalną wartość – 300dp szerokości i 200dp wysokości.
  • Nawet jeśli modyfikator size chce użyć rozmiaru 50dp, nadal musi stosować się do przychodzących minimalnych ograniczeń. Dlatego modyfikator size będzie też generować dokładne granice ograniczeń 300 na 200, ignorując wartość podaną w modyfikatorze size.
  • Element Image stosuje się do tych granic i zgłasza rozmiar 300 na 200, który jest przekazywany aż do góry drzewa.

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

Ten fragment kodu generuje te dane wyjściowe:

Mały niebieski kwadrat wyśrodkowany w kontenerze nadrzędnym.
Rysunek 15. Element Image jest wyśrodkowany i ma rozmiar 50dp.
  • Modyfikator fillMaxSize dostosowuje ograniczenia, aby ustawić zarówno minimalną szerokość, jak i wysokość na maksymalną wartość – 300dp szerokości i 200dp wysokości.
  • Modyfikator wrapContentSize resetuje minimalne ograniczenia. Dlatego, chociaż fillMaxSize spowodował stałe ograniczenia, wrapContentSize resetuje je z powrotem do ograniczeń określonych. Następny węzeł może teraz ponownie zajmować całą przestrzeń lub być mniejszy niż cała przestrzeń.
  • Modyfikator size ustawia ograniczenia na minimalne i maksymalne granice 50.
  • Element Image przyjmuje rozmiar 50 na 50, a modyfikator size przekazuje go 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 do niego przekazane. Rozmiar, który przekazuje do swoich 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 funkcji kompozycyjnej i wyśrodkować ją 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:

Okrąg, który jest nieprawidłowo przycięty z powodu kolejności modyfikatorów.
Rysunek 16. Nieprawidłowo przycięty kształt z powodu kolejności modyfikatorów.
  • Modyfikator clip nie zmienia ograniczeń.
  • Modyfikator padding zmniejsza maksymalne ograniczenia.
  • Modyfikator size ustawia wszystkie ograniczenia na 100dp.
  • Element Image stosuje się do tych ograniczeń i zgłasza rozmiar 100dp na 100dp.
  • Modyfikator padding dodaje 10dp ze wszystkich stron do rozmiaru zgłoszonego przez element Image, więc układ z dopełnieniem zgłasza szerokość i wysokość 120dp.
  • W fazie rysowania modyfikator clip działa na płótnie o wymiarach 120dp na 120dp. Tworzy maskę koła o tym rozmiarze.
  • Modyfikator padding wstawia następnie treść o 10dp ze wszystkich stron, co zmniejsza rozmiar płótna dla elementu Image do 100dp na 100dp.
  • Element Image jest rysowany na tym mniejszym płótnie. Obraz jest przycinany na podstawie oryginalnego koła o średnicy 120dp, więc dane wyjściowe nie są okrągłe.