В Compose можно объединять несколько модификаторов, чтобы изменить внешний вид и поведение composable-функции. Эти цепочки модификаторов могут влиять на ограничения, передаваемые composable-функциям, которые определяют границы ширины и высоты.
На этой странице описывается, как цепочки модификаторов влияют на ограничения и, в свою очередь, на измерение и размещение составных элементов.
Модификаторы в дереве интерфейса
Чтобы понять, как модификаторы влияют друг на друга, полезно визуализировать, как они появляются в дереве пользовательского интерфейса, которое генерируется на этапе композиции. Подробнее об этом рассказывается в разделе Состав.
В дереве пользовательского интерфейса модификаторы можно представить как узлы-оболочки для узлов макета:
Если добавить к composable-функции несколько модификаторов, они образуют цепочку. Когда вы связываете несколько модификаторов, каждый узел модификатора оборачивает остальную часть цепочки и узел макета внутри. Например, если вы объединяете модификаторы clip и size, узел модификатора clip оборачивает узел модификатора size, который, в свою очередь, оборачивает узел макета Image.
На этапе макета алгоритм, который обходит дерево, остается прежним, но также посещается каждый узел модификатора. Таким образом, модификатор может изменить требования к размеру и размещение модификатора или узла макета, который он оборачивает.
Как показано на рисунке 2, реализация composable-функций Image и Text состоит из цепочки модификаторов, которые оборачивают один узел макета.
Реализации Row и Column – это узлы макета, которые описывают, как размещать дочерние элементы.
Подведем итоги:
- Модификаторы оборачивают один модификатор или узел макета.
- Узлы макета могут содержать несколько дочерних узлов.
В следующих разделах описывается, как использовать эту ментальную модель для рассуждений о цепочке модификаторов и о том, как она влияет на размер составных элементов.
Ограничения на этапе макета
Этап макета использует трехшаговый алгоритм для определения ширины, высоты и координат x, y каждого узла макета:
- Измерение дочерних элементов. Узел измеряет свои дочерние элементы, если они есть.
- Определение собственного размера. На основе этих измерений узел определяет собственный размер.
- Размещение дочерних элементов. Каждый дочерний узел размещается относительно положения узла.
Constraints помогает найти правильные размеры узлов на первых двух шагах алгоритма. Ограничения определяют минимальные и максимальные границы ширины и высоты узла. Когда узел определяет свой размер, его измеренный размер должен находиться в этом диапазоне.
Типы ограничений
Ограничение может быть одним из следующих:
- Ограниченный. Узел имеет максимальную и минимальную ширину и высоту.
- Unbounded. Узел не ограничен по размеру. Максимальная ширина и высота не ограничены.
- Точный. Узел должен соответствовать точному размеру. Минимальное и максимальное значения совпадают.
- Комбинированное ограничение. Узел следует комбинации предыдущих типов ограничений. Например, можно ограничить ширину, но не высоту, или задать точную ширину и ограничить высоту.
В следующем разделе описывается, как эти ограничения передаются от родительского элемента к дочернему.
Как ограничения передаются от родительского элемента к дочернему
На первом этапе алгоритма, описанном в разделе Ограничения на этапе макета, ограничения передаются от родительского элемента к дочернему в дереве пользовательского интерфейса.
Когда родительский узел измеряет свои дочерние элементы, он предоставляет эти ограничения каждому дочернему элементу, чтобы сообщить им, насколько большими или маленькими они могут быть. Затем, когда он определяет свой собственный размер, он также придерживается ограничений, которые были переданы его собственными родителями.
В общих чертах алгоритм работает следующим образом:
- Чтобы определить размер, который он на самом деле хочет занять, корневой узел в дереве пользовательского интерфейса измеряет свои дочерние элементы и пересылает те же ограничения своему первому дочернему элементу.
- Если дочерний элемент является модификатором, который не влияет на измерение, он пересылает ограничения следующему модификатору. Ограничения передаются по цепочке модификаторов как есть, пока не будет достигнут модификатор, влияющий на измерение. Затем ограничения изменяются соответствующим образом.
- Когда узел не имеет дочерних элементов (так называемый листовой узел), он определяет свой размер на основе переданных ограничений и возвращает этот разрешенный размер своему родительскому элементу.
- Родительский виджет адаптирует свои ограничения на основе размеров дочернего виджета и вызывает следующий дочерний виджет с этими скорректированными ограничениями.
- После того как все дочерние элементы родительского элемента будут измерены, родительский узел принимает решение о своем собственном размере и сообщает об этом своему родительскому элементу.
- Таким образом, все дерево обходится в глубину. В конце концов все узлы определили свои размеры, и этап измерения завершен.
Подробный пример смотрите в видео Ограничения и порядок модификаторов.
Модификаторы, влияющие на ограничения
В предыдущем разделе вы узнали, что некоторые модификаторы могут влиять на размер ограничений. В следующих разделах описаны конкретные модификаторы, влияющие на ограничения.
Модификатор size
Модификатор size объявляет предпочтительный размер контента.
Например, следующее дерево пользовательского интерфейса должно быть отрисовано в контейнере размером 300dp на 200dp. Ограничения заданы в виде диапазонов: ширина от 100dp до 300dp, высота от 100dp до 200dp.
Модификатор size адаптирует входящие ограничения в соответствии с переданным ему значением.
В этом примере значение – 150dp:
size, корректирующий ограничения до 150dp.Если ширина и высота меньше наименьшей границы ограничения или больше наибольшей границы ограничения, модификатор максимально точно соответствует переданным ограничениям, при этом соблюдая переданные ограничения:
size, максимально соответствующий переданному ограничению.Обратите внимание, что объединять несколько модификаторов size нельзя. Первый модификатор size устанавливает фиксированное значение для минимального и максимального ограничений. Даже если второй модификатор размера запрашивает меньший или больший размер, он все равно должен придерживаться точных переданных границ, поэтому он не будет переопределять эти значения:
size, в которой второе переданное значение (50dp) не переопределяет первое (100dp).Модификатор requiredSize
Если вам нужно, чтобы узел переопределял входящие ограничения, используйте модификатор requiredSize вместо size. Модификатор requiredSize заменяет входящие ограничения и передает указанный вами размер в качестве точных границ.
Когда размер передается обратно вверх по дереву, дочерний узел будет центрирован в доступном пространстве:
requiredSize переопределяет ограничения, заданные модификатором size.Модификаторы width и height
Модификатор size адаптирует как ширину, так и высоту ограничений. С помощью модификатора width можно задать фиксированную ширину, но оставить высоту неопределенной.
Аналогично с помощью модификатора height можно задать фиксированную высоту, но оставить ширину неопределенной:
width и height, задающие фиксированную ширину и высоту соответственно.Модификатор sizeIn
Модификатор sizeIn позволяет задать точные минимальные и максимальные ограничения для ширины и высоты. Если вам нужен точный контроль над ограничениями, используйте модификатор sizeIn.
sizeIn с заданными значениями minWidth, maxWidth, minHeight и maxHeight.Примеры
В этом разделе показаны и объяснены выходные данные нескольких фрагментов кода с цепочками модификаторов.
Image( painterResource(R.drawable.hero), contentDescription = null, Modifier .fillMaxSize() .size(50.dp) )
Этот фрагмент кода дает следующий результат:
Image заполняет максимальный размер.- Модификатор
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) )
Этот фрагмент кода дает следующий результат:
Image центрируется и масштабируется до размера 50dp.- Модификатор
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соблюдает эти ограничения и сообщает о размере100dpна100dp.- Модификатор
paddingдобавляет10dpсо всех сторон к размеру, сообщаемомуImage, поэтому макет с отступами сообщает ширину и высоту120dp. - Теперь, на этапе рисования, модификатор
clipдействует на холсте размером120dpна120dp. Будет создана круглая маска указанного размера. - Затем модификатор
paddingвставляет свое содержимое на10dpсо всех сторон, что уменьшает размер холста дляImageдо100dpна100dp. Imageрисуется на этом меньшем холсте. Изображение обрезается по исходному кругу120dp, поэтому результат получается некруглым.