Ограничения и порядок модификаторов

В Compose можно объединять несколько модификаторов в цепочку, чтобы изменить внешний вид компонуемого объекта. Эти цепочки модификаторов могут влиять на ограничения, передаваемые компонуемым объектам, которые определяют границы ширины и высоты.

На этой странице описывается, как цепочечные модификаторы влияют на ограничения и, в свою очередь, на измерение и размещение компонуемых объектов.

Модификаторы в дереве пользовательского интерфейса

Чтобы понять, как модификаторы влияют друг на друга, полезно визуализировать их отображение в дереве пользовательского интерфейса, которое генерируется на этапе композиции. Подробнее см. в разделе «Композиция» .

В дереве пользовательского интерфейса вы можете визуализировать модификаторы как узлы-оболочки для узлов макета:

Код для составных элементов и модификаторов, а также их визуальное представление в виде дерева пользовательского интерфейса.
Рисунок 1. Модификаторы, оборачивающие узлы макета в дереве пользовательского интерфейса.

Добавление более одного модификатора к компонуемому объекту создаёт цепочку модификаторов. При объединении нескольких модификаторов в цепочку каждый узел модификатора оборачивает остальную часть цепочки и узел макета внутри . Например, при объединении в цепочку clip и модификатора size узел модификатора clip оборачивает узел модификатора size , который затем оборачивает узел макета Image .

На этапе компоновки алгоритм обхода дерева остаётся прежним, но каждый узел-модификатор также посещается. Таким образом, модификатор может изменять требования к размеру и расположению модификатора или узла компоновки, которые он оборачивает.

Как показано на рисунке 2, реализация компонуемых элементов « Image и Text представляет собой цепочку модификаторов, обёрнутых вокруг одного узла макета. Реализации элементов Row и Column — это просто узлы макета, описывающие, как размещать дочерние элементы.

Древовидная структура, как и прежде, но теперь каждый узел представляет собой простую схему со множеством модификаторов, оборачивающих его узлами.
Рисунок 2. Та же древовидная структура, что и на рисунке 1, но с компонуемыми элементами в дереве пользовательского интерфейса, визуализированными как цепочки модификаторов.

Подведем итог:

  • Модификаторы оборачивают один модификатор или узел макета.
  • Узлы макета могут содержать несколько дочерних узлов.

В следующих разделах описывается, как использовать эту ментальную модель для рассуждений о цепочке модификаторов и о том, как она влияет на размер компонуемых объектов.

Ограничения на этапе макетирования

Фаза макета следует трехшаговому алгоритму для нахождения ширины, высоты и координат x, y каждого узла макета:

  1. Измерение дочерних элементов : узел измеряет своих дочерних элементов, если таковые имеются.
  2. Определите свой размер : на основе этих измерений узел определяет свой собственный размер.
  3. Размещение дочерних узлов : каждый дочерний узел размещается относительно собственного положения узла.

Constraints помогают определить правильные размеры узлов на первых двух этапах алгоритма. Ограничения определяют минимальные и максимальные границы ширины и высоты узла. Когда узел определяет свой размер, его измеренный размер должен находиться в пределах этого диапазона.

Типы ограничений

Ограничение может быть одним из следующих:

  • Ограниченный : узел имеет максимальную и минимальную ширину и высоту.
Ограничения разных размеров внутри контейнера.
Рисунок 3. Ограничения.
  • Неограниченный : узел не ограничен никакими размерами. Максимальные границы ширины и высоты равны бесконечности.
Неограниченные ограничения, ширина и высота которых равны бесконечности. Ограничения выходят за пределы контейнера.
Рисунок 4. Неограниченные ограничения.
  • Точно : узлу предписано точное соответствие размеру. Минимальное и максимальное значения устанавливаются равными.
Точные ограничения, соответствующие точным требованиям к размерам внутри контейнера.
Рисунок 5. Точные ограничения.
  • Комбинация : узел следует комбинации перечисленных выше типов ограничений. Например, ограничение может ограничивать ширину, допуская неограниченную максимальную высоту, или задавать точную ширину, но ограничивать высоту.
Два контейнера, которые показывают комбинации ограниченных и неограниченных ограничений, а также точные значения ширины и высоты.
Рисунок 6. Сочетания ограниченных и неограниченных ограничений и точных значений ширины и высоты.

В следующем разделе описывается, как эти ограничения передаются от родителя к потомку.

Как ограничения передаются от родителя к потомку

На первом этапе алгоритма, описанного в разделе «Ограничения на этапе макета» , ограничения передаются от родителя к дочернему элементу в дереве пользовательского интерфейса.

Когда родительский узел измеряет свои дочерние узлы, он предоставляет эти ограничения каждому дочернему узлу, чтобы сообщить им, насколько большими или маленькими они могут быть. Затем, при определении собственного размера, он также придерживается ограничений, переданных его родительскими узлами.

На высоком уровне алгоритм работает следующим образом:

  1. Чтобы определить, какой размер он фактически хочет занять, корневой узел в дереве пользовательского интерфейса измеряет свои дочерние элементы и пересылает те же ограничения своему первому дочернему элементу.
  2. Если дочерний модификатор не влияет на измерение, он передаёт ограничения следующему модификатору. Ограничения передаются по цепочке модификаторов без изменений, если только не будет достигнут модификатор, влияющий на измерение. После этого ограничения соответствующим образом изменяются.
  3. При достижении узла, не имеющего дочерних узлов (называемого «конечным узлом»), он определяет его размер на основе переданных ограничений и возвращает этот решенный размер своему родительскому узлу.
  4. Родительский элемент адаптирует свои ограничения на основе измерений этого дочернего элемента и вызывает своего следующего дочернего элемента с этими скорректированными ограничениями.
  5. После того как все дочерние элементы родителя измерены, родительский узел определяет свой собственный размер и сообщает его своему родителю.
  6. Таким образом, всё дерево обходит в глубину. В конце концов, все узлы определяют свои размеры, и этап измерения завершается.

Подробный пример смотрите в видеоролике « Ограничения и порядок модификаторов» .

Модификаторы, влияющие на ограничения

В предыдущем разделе вы узнали, что некоторые модификаторы могут влиять на размер ограничений. В следующих разделах описываются конкретные модификаторы, влияющие на ограничения.

модификатор size

Модификатор size объявляет предпочтительный размер содержимого.

200dp , следующее дерево пользовательского интерфейса должно быть отображено в контейнере 200dp 300dp . Ограничения ограничены: ширина может варьироваться от 100dp , а высота 300dp от 100dp :

Часть дерева пользовательского интерфейса с модификатором размера, обёртывающим узел макета, и представление ограничивающих ограничений, установленных модификатором размера в контейнере.
Рисунок 7. Ограничения в дереве пользовательского интерфейса и его представление в контейнере.

Модификатор size адаптирует входящие ограничения к переданному ему значению. В этом примере значение равно 150dp :

То же, что и на рисунке 7, за исключением того, что модификатор размера адаптирует входящие ограничения для соответствия переданному ему значению.
Рисунок 8. Модификатор size , устанавливающий ограничения до 150dp .

Если ширина и высота меньше наименьшего ограничения или больше наибольшего ограничения, модификатор максимально точно соответствует переданным ограничениям, при этом соблюдая переданные ограничения:

Два дерева пользовательского интерфейса и их соответствующие представления в контейнерах. В первом случае модификатор размера принимает входящие ограничения; во втором случае модификатор размера максимально точно адаптируется к слишком большим ограничениям, что приводит к ограничениям, заполняющим контейнер.
Рисунок 9. Модификатор size , максимально соответствующий переданному ограничению.

Обратите внимание, что цепочка из нескольких модификаторов size не работает. Первый модификатор size устанавливает фиксированные значения как для минимального, так и для максимального размера. Даже если второй модификатор размера запрашивает меньший или больший размер, он всё равно должен точно соответствовать переданным границам, поэтому он не переопределит эти значения:

Цепочка из двух модификаторов размера в дереве пользовательского интерфейса и её представление в контейнере, которое является результатом первого переданного значения, а не второго.
Рисунок 10. Цепочка из двух модификаторов size , в которой второе переданное значение ( 50dp ) не переопределяет первое значение ( 100dp ).

Модификатор requiredSize

Используйте модификатор requiredSize вместо size , если вам нужно, чтобы ваш узел переопределил входящие ограничения. Модификатор requiredSize заменяет входящие ограничения и передаёт указанный вами размер в качестве точных границ.

Когда размер передается обратно вверх по дереву, дочерний узел будет центрирован в доступном пространстве:

Размер и модификатор requiredSize объединены в древовидную структуру пользовательского интерфейса, а соответствующее представление в контейнере. Ограничения модификатора requiredSize переопределяют ограничения модификатора размера.
Рисунок 11. Модификатор requiredSize переопределяет входящие ограничения модификатора size .

модификаторы width и height

Модификатор size регулирует как ширину, так и высоту ограничений. С помощью модификатора width можно задать фиксированную ширину, не определяя высоту. Аналогично, с помощью модификатора height можно задать фиксированную высоту, не определяя ширину:

Два дерева пользовательского интерфейса: одно с модификатором ширины и его представлением в контейнере, а другое с модификатором высоты и его представлением.
Рисунок 12. Модификатор width и модификатор height задают фиксированную ширину и высоту соответственно.

модификатор sizeIn

Модификатор sizeIn позволяет задать точные минимальные и максимальные ограничения по ширине и высоте. Используйте модификатор sizeIn если вам требуется точный контроль над ограничениями.

Дерево пользовательского интерфейса с модификатором sizeIn, устанавливающим минимальную и максимальную ширину и высоту, и его представление в контейнере.
Рисунок 13. Модификатор sizeIn с установленными minWidth , maxWidth , minHeight и maxHeight .

Примеры

В этом разделе показаны и объяснены выходные данные нескольких фрагментов кода с цепочкой модификаторов.

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

Этот фрагмент выводит следующий результат:

  • Модификатор fillMaxSize изменяет ограничения, устанавливая как минимальную ширину, так и высоту на максимальное значение — 300dp по ширине и 200dp по высоте.
  • Несмотря на то, что модификатор size хочет использовать размер 50dp , ему всё равно необходимо соблюдать входящие минимальные ограничения. Поэтому модификатор size также выведет точные границы ограничений 300 на 200 , фактически игнорируя значение, указанное в модификаторе size .
  • Image следует этим границам и сообщает размер 300 на 200 , который передается по всему дереву.

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

Этот фрагмент выводит следующий результат:

  • Модификатор fillMaxSize адаптирует ограничения, чтобы установить как минимальную ширину, так и высоту до максимального значения — 300dp по ширине и 200dp по высоте.
  • Модификатор wrapContentSize сбрасывает минимальные ограничения. Таким образом, в то время как fillMaxSize приводил к фиксированным ограничениям, wrapContentSize сбрасывает их обратно к ограниченным ограничениям . Следующий узел теперь может снова занимать всё пространство или быть меньше его.
  • Модификатор size устанавливает ограничения на минимальное и максимальное значение 50 .
  • Image принимает размер 50 на 50 , и модификатор size его перенаправляет.
  • Модификатор wrapContentSize имеет особое свойство. Он помещает свой дочерний элемент в центр минимальных границ , которые были ему переданы. Таким образом, размер, который он передает своим родительским элементам, равен минимальным границам, которые были ему переданы.

Объединив всего три модификатора, можно определить размер компонуемого элемента и разместить его по центру родительского элемента.

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

Этот фрагмент выводит следующий результат:

  • Модификатор clip не изменяет ограничения.
    • Модификатор padding снижает максимальные ограничения.
    • Модификатор size устанавливает все ограничения на 100dp .
    • Image соответствует этим ограничениям и имеет размер 100 на 100dp .
    • Модификатор padding добавляет 10dp ко всем размерам, поэтому он увеличивает сообщаемую ширину и высоту на 20dp .
    • На этапе рисования модификатор clip действует на холст размером 120 на 120dp . Таким образом, он создаёт круглую маску такого размера .
    • Затем модификатор padding вставляет свое содержимое на 10dp для всех размеров, таким образом уменьшая размер холста до 100 на 100dp .
    • Image рисуется на этом холсте. Изображение обрезано по исходному кругу 120dp , поэтому результат получается некруглым.