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

В 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 задает предпочтительный размер контента.

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

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

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

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

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

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

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

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

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

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

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

Модификатор size и requiredSize, объединенные в цепочку в дереве интерфейса, и соответствующее представление в контейнере. Ограничения, заданные с помощью модификатора requiredSize, переопределяют ограничения, заданные с помощью модификатора size.
Рисунок 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)
)

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

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

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

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

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

С помощью всего трех модификаторов можно задать размер composable-функции и центрировать ее в родительском элементе.

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

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

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