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 będzie otaczać pozostałą część łańcucha i węzeł układu. Jeśli na przykład połączysz modyfikatory clip i size, węzeł modyfikatora clip będzie zawierać węzeł modyfikatora size, który z kolei będzie zawierać 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. W ten sposób modyfikator może zmienić wymagania dotyczące rozmiaru i umiejscowienia modyfikatora lub węzła układu, który otacza.

Jak widać na rysunku 2, implementacja funkcji kompozycyjnych Image i Text składa się z łańcucha modyfikatorów, które opakowują pojedynczy węzeł układu.

Implementacje Row i Column to węzły układu, które opisują, jak rozmieścić elementy podrzędne.

Struktura drzewa z poprzedniego przykładu, ale teraz każdy węzeł jest prostym układem z wieloma węzłami opakowującymi modyfikatory.
Rysunek 2. Ta sama struktura drzewa co na rysunku 1, ale z funkcjami kompozycyjnymi w drzewie UI zwizualizowanymi jako łańcuchy modyfikatorów.

Podsumowując:

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

W sekcjach poniżej opisujemy, jak używać tego modelu mentalnego do analizowania łańcucha modyfikatorów i jego wpływu na rozmiar funkcji kompozycyjnych.

Ograniczenia na etapie układu

Faza układu wykorzystuje 3-etapowy algorytm, aby znaleźć szerokość, wysokość i współrzędne x, 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. Decydowanie o własnym rozmiarze: na podstawie tych pomiarów węzeł decyduje o własnym rozmiarze.
  3. Umieszczanie elementów podrzędnych: każdy węzeł podrzędny jest umieszczany względem pozycji węzła.

Constraints pomagają znaleźć odpowiednie rozmiary węzłów podczas pierwszych 2 kroków algorytmu. Ograniczenia określają minimalne i maksymalne granice szerokości i wysokości węzła. Gdy węzeł określa swój rozmiar, 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ł jest zgodny z kombinacją poprzednich typów ograniczeń. Na przykład ograniczenie może określać szerokość, ale pozwalać na nieograniczoną maksymalną wysokość, lub ustawiać dokładną szerokość, ale określać 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 opisano, jak te ograniczenia są przekazywane z elementu nadrzędnego do elementu podrzędnego.

Jak ograniczenia są przekazywane z elementu nadrzędnego do elementu 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 węzły podrzędne, przekazuje te ograniczenia każdemu z nich, aby poinformować je, jak duże lub małe mogą być. Następnie, gdy określa swój rozmiar, przestrzega też ograniczeń przekazanych przez 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 żadnych węzłów podrzędnych (nazywany „węzłem liścia”), określa on 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 wymiarów tego elementu podrzędnego i wywołuje następny element podrzędny z tymi dostosowanymi ograniczeniami.
  5. Po zmierzeniu wszystkich elementów podrzędnych węzeł nadrzędny określa swój rozmiar i przekazuje go do swojego węzła nadrzędnego.
  6. W ten sposób całe drzewo jest przeszukiwane w głąb. W końcu wszystkie węzły określą swoje rozmiary i etap pomiaru zostanie zakończony.

Szczegółowy przykład znajdziesz w filmie 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.

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 przekazanej wartości. W tym przykładzie wartość to 150dp:

Tak samo jak na rysunku 7, z tą różnicą, że modyfikator size dostosowuje przychodzące ograniczenia do przekazanej mu wartości.
Rysunek 8. Modyfikator size dostosowuje ograniczenia do 150dp.

Jeśli szerokość i wysokość są mniejsze niż najmniejsza granica ograniczenia lub większe niż największa granica ograniczenia, modyfikator dopasowuje przekazane ograniczenia tak dokładnie, jak to możliwe, zachowując przy tym ograniczenia przekazane w:

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, który w jak największym stopniu przestrzega przekazanego ograniczenia.

Pamiętaj, że łączenie wielu 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 żąda mniejszego lub większego rozmiaru, nadal musi przestrzegać 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. Ciąg 2 modyfikatorów size, w którym druga przekazana wartość (50dp) nie zastępuje pierwszej (100dp).

Modyfikator requiredSize

Jeśli chcesz, aby węzeł zastępował ograniczenia przychodzące, 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 w górę drzewa, węzeł podrzędny zostanie wyśrodkowany w dostępnej przestrzeni:

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ępuje ograniczenia przychodzące 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. Modyfikator width i modyfikator height ustawiają 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 precyzyjnej 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 objaśniamy dane wyjściowe kilku fragmentów kodu z modyfikatorami połączonymi w łańcuch.

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

Ten fragment kodu generuje te dane wyjściowe:

Niebieski kwadrat, który wypełnia kontener nadrzędny.
Rysunek 14. Image wypełnia maksymalny rozmiar w wyniku działania ł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.
  • Mimo że modyfikator size chce użyć rozmiaru 50dp, musi przestrzegać przychodzących ograniczeń minimalnych. Dlatego modyfikator size również zwróci dokładne granice ograniczeń 300 na 200, skutecznie ignorując wartość podaną w modyfikatorze size.
  • Image przestrzega tych ograniczeń i zgłasza rozmiar 300 na 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:

Mały niebieski kwadrat wyśrodkowany w kontenerze nadrzędnym.
Rysunek 15. Image jest wyśrodkowany i ma rozmiar 50dp.
  • 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. Tak więc, podczas gdy fillMaxSize spowodowało stałe ograniczenia, wrapContentSize przywraca je do ograniczeń określonych. Kolejny 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.
  • 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 w ś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 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ły kształt, 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.
  • Image przestrzega tych ograniczeń i zgłasza rozmiar 100dp na 100dp.
  • Modyfikator padding dodaje 10dp ze wszystkich stron do rozmiaru zgłoszonego przez Image, więc układ z dopełnieniem zgłasza szerokość i wysokość 120dp.
  • Teraz, na etapie rysowania, modyfikator clip działa na obszarze roboczym o wymiarach 120dp na 120dp. Spowoduje to utworzenie okrągłej maski o tym rozmiarze.
  • Modyfikator padding wstawia następnie zawartość z dopełnieniem 10dp ze wszystkich stron, co zmniejsza rozmiar obszaru roboczego dla Image do 100dp na 100dp.
  • Image jest rysowany na mniejszym obszarze roboczym. Obraz jest przycinany na podstawie pierwotnego okręgu o średnicy 120dp, więc wynik nie jest okrągły.