- PVSM.RU - https://www.pvsm.ru -

Привет! Меня зовут Гавриил, я Android-лид Touch Instinct.
В марте Google выкатил релизное обновление ConstraintLayout. Презентовали его еще на прошлом Google I/O [1]. С того момента прошел почти год, ConstraintLayout стал лучше, быстрее и оброс новыми возможностями. Например, приятно, что появилась возможность объединения элементов в цепи — это позволяет использовать ConstraintLayout вместо LinearLayout.
О всех новых и старых возможностях ConstraintLayout я и постараюсь рассказать в этой статье. Предупрежу сразу — статья будет длинная и других семи частей не будет. Про визуальный редактор ConstraintLayout в статье не будет ни слова — только XML-разметка (по старинке) и немного кода.
1. Добавляем в проект [2]
2. Contsraints. Привязываем элементы друг к другу [3]
3. Задаем размеры View [4]
4. Задаем размеры View на основе соотношения сторон [5]
5. Выставляем относительное расположение View внутри ConstraintLayout [6]
6. Особенности привязки линий [7]
7. Особенности привязки по Baseline [8]
8. Создаем цепи (chains) [9]
8.1. Стиль spread [10]
8.2. Стиль spread_inside [11]
8.3. Стиль packed [12]
8.4. Стиль weighted [13]
9. Указываем отступы с учетом скрытых элементов [14]
10. Разбираемся с Guidelines [15]
11. Настраиваем параметры ConstraintLayout из кода [16]
12. Коротко об анимации [17]
13. Стоит ли пользоваться ConstraintLayout? [18]
Android Studio → Settings(Preferences) → Appearance & Behavior → System Settings → Android SDK → SDK Tools → Support Repository;build.gradle модуля проекта:
dependencies {
...
compile 'com.android.support.constraint:constraint-layout:1.0.2'
...
}
Теперь можно использовать ConstraintLayout у себя в проекте:
<android.support.constraint.ConstraintLayout
android:id="@+id/my_first_constraint_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/some_constraint_layout_element"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello world!"/>
</android.support.constraint.ConstraintLayout>
Constraints — это линии, на основе которых располагается view внутри ConstraintLayout. Constraints могут быть привязаны к сторонам самого ConstraintLayout или к сторонам других view внутри ConstraintLayout. Constraints можно разделить на вертикальные и горизонтальные.
Горизонтальные constraints:
Вертикальные constraints:
Вертикальное и горизонтальное constraints друг с другом не связаны.
Напомню, что Baseline — это линия выравнивания контента элемента. Пример — для TextView это линия строки, на которой пишется текст. Если у view выставлен Baseline сonstraint, то базовая линия элемента будет находиться на уровне базовой линии view, к которой привязан сonstraint.
Для начала, проще всего рассматривать сonstraints, как стороны view. То есть можно, например, привязать левую сторону view B к правой стороне view A — тогда view B будет располагаться справа от view A.
Общий формат атрибутов для привязки сonstraint выглядит следующим образом:
app:layout_constraint{X}_to{Y}Of="{Z}"
Где:
parent, если привязать нужно к стороне ContraintLayout.Пример привязок:
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- Левый constraint view_1 привязан к левой стороне ConstraintLayout.
Верхний constraint view_1 — к верхней стороне ConstraintLayout.
Правый и нижний constraint не привязаны -->
<TextView
android:id="@+id/view_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="view_1"
android:textSize="24sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<!-- Левый constraint у view_2 привязан к правой стороне view_1.
Constraint базовой линии view_2 — к базовой линии view_1.
Нижний и верхний constraint привязывать нельзя, так как есть привязка по базовой линии.
Правый constraint не привязан. -->
<TextView
android:id="@+id/view_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="view_2"
android:textSize="14sp"
app:layout_constraintLeft_toRightOf="@id/view_1"
app:layout_constraintBaseline_toBaselineOf="@id/view_1"/>
<!-- Левый constraint view_3 привязан к правой стороне view_1.
Верхний constraint view_3 — к нижней стороне view_1.
Правый и нижний constraint не привязаны. -->
<TextView
android:id="@+id/view_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="view_3"
android:textSize="14sp"
app:layout_constraintTop_toBottomOf="@id/view_1"
app:layout_constraintLeft_toRightOf="@id/view_1"/>
<!-- Левый constraint view_4 привязан к правой стороне view_2.
Нижний constraint view_4 — к нижней стороне view_2.
Правый и верхний constraint не привязаны. -->
<TextView
android:id="@+id/view_4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="view_4"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="@id/view_1"
app:layout_constraintLeft_toRightOf="@id/view_2"/>
</android.support.constraint.ConstraintLayout>

Основные правила привязки сторон:
Start и End, Left и Right, Top и Bottom. То есть, нельзя, например, привязать Left к Start или Baseline к Top;Baseline можно привязать только к Baseline другой view;layout_constraintRight_toLeftOf="parent". ConstraintLayout обработает такую привязку, но как себя при этом поведет, сложно предсказать.Чтобы задать размеры view, используются обязательные атрибуты layout_width и layout_height, и необязательные атрибуты layout_constraintWidth_default и layout_constraintHeight_default.
Значение атрибутов layout_constraintWidth_default и layout_constraintHeight_default по умолчанию равно spread.
Размер view могут быть указан, как (на примере высоты):
layout_height="100dp" — обозначим, как fixed_size. View будет указанного размера;layout_height="wrap_content" — обозначим, как any_size. Размер вычисляется самой view, может быть любым;layout_height="0dp" + layout_constraintHeight_default="spread" — обозначим, как match_constraint_spread. Размер view будет равен расстоянию между constraints. Для высоты, например, это расстояние между верхним constraint и нижним constraint;layout_height="0dp" + layout_constraintHeight_default="wrap" — обозначим, как match_constraint_wrap. Размер вычисляется самой view, но не может выйти за рамки constraints.Важно! Указывать размер match_parent или fill_parent запрещено. Чтобы размер view совпадал с размерами ConstraintLayout, достаточно просто привязать constraints к сторонам ConstraintLayot и использовать размер match_constraint_spread.
Если указан размер match_constraint_wrap или match_constraint_spread, стоит учесть, что:
Left и Right или Start и End. Для высоты — Top и Bottom;layout_constraintWidth_min, layout_constraintHeight_min, layout_constraintWidth_max, layout_constraintHeight_max;Baseline constraint — вероятно, высота элемента будет рассчитываться неверно.Для других типов размеров стоит учитывать, что:

ConstraintLayout позволяет рассчитывать высоту или ширину view на основе заданного соотношения сторон. То есть, например, при соотношении сторон 16:9, если высота будет 900dp, то ширина рассчитается, как 1600dp.
За это отвечает атрибут layout_constraintDimensionRatio. Задать соотношение сторон можно в двух форматах: текстовом 16:9 или числовом 1.8. При этом перед значением можно указать символ стороны, которая находится в числителе соотношения. Например, H,16:9 будет означать, что 16 — это значение, соотвествующее высоте (H), а 9 — ширине (W).
Значение в layout_constraintDimensionRatio учитывается при расчете размеров view, только если хотя бы одна из сторон выставлена в match_constraint_wrap или match_constraint_spread.
Пример:
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Соотношение сторон 16:9
Все constraints у view привязаны к сторонам ConstraintLayout.
Ширина view будет равна ширине ConstraintLayout,
так как layout_width — match_constraint_spread.
Высота view будет рассчитана на основе соотношения,
так как layout_height — any_size. -->
<ImageView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
app:layout_constraintDimensionRatio="16:9"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:background="@drawable/big_widescreen_image"/>
</android.support.constraint.ConstraintLayout>

Если у view привязать два горизонтальных constraints, то ей можно выставить горизонтальное относительное расположение. То же применимо и для вертикальных constraints.
За горизонтальное расположение отвечает атрибут layout_constraintHorizontal_bias, за вертикальное — layout_constraintVertical_bias. Указывается относительное расположение значением от 0 до 1.
По сути, это более гибкая замена атрибута layout_gravity. Например, для горизонтального расположения 0 будет означать расположение крайне слева, 0.5 — по центру, 1 — крайне справа. По умолчанию — 0.5.
Теперь, например, выставим значение 0.3. Это будет означать, что 30% не заполненного view места будет слева от view, а 70% — справа. Если же размер view больше размера расстояния между constraints, то 30% выходящего за constraints размера будет слева от ограничений, а 70% — справа.

Небольшое важное замечание: если в манифесте выставлена поддержка RTL языков, то layout_constraintHorizontal_bias вместо "слева" будет располагать элементы "от начала", а вместо "справа" — "от конца". То есть тем, кто поддерживает RTL языки стоит учитывать, что явно выставить расположение "слева" и "справа" не выйдет. По крайней мере, я такой возможности не нашел.
Пример:
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Горизонтальный bias выставлен в 0.3 (30% отступ слева/от начала).
Вертикальный bias — в 1 (снизу).
Constraints привязаны к сторонам контейнера.
Ширина — match_constraint_wrap, высота — match_constraint_wrap. -->
<TextView
android:id="@+id/view_1"
android:text="view_1"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintWidth_default="wrap"
app:layout_constraintHeight_default="wrap"
android:scaleType="centerCrop"
app:layout_constraintHorizontal_bias="0.3"
app:layout_constraintVertical_bias="1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</android.support.constraint.ConstraintLayout>
После презентации ConstraintLayout его часто сравнивали с RelativeLayout. Но, на самом деле, у них принципиально разные расчеты расположения элементов. В RelativeLayout у view просто указывается, с какой стороны другой view ей нужно находиться — "слева от", "справа от" и т.д. В ConstraintLayout constraints привязываются к сторонам других views и расположение view зависит от того, как ее constraints будут рассчитаны.
Для расположения constraint сперва рассчитывается расположение view, к которой этот constraint привязан. А для расположения view сперва рассчитываются все указанные для нее constraints. Циклические зависимости view и constraints при этом запрещены, так что, фактически, внутри ConstraintLayout строится направленный ациклический граф зависимостей constraints от view и view от constraints. Все расчеты производятся последовательно, начиная от не зависимых элементов графа.
Для простоты советую разделять вертикальные и горизонтальные зависимости на две независимые группы, как-будто внутри ConstraintLayout строится отдельно граф вертикальных зависимостей и отдельно — горизонтальных.
Но, вообще говоря, стоит понимать, что вертикальные параметры view, косвенно, могут зависеть от горизонтальных параметров другой view. Пример — расчет размера на основе соотношения сторон: при изменении ширины view меняется и ее высота. То есть, если у view изменится ширина из-за изменений ее горизонтальных constraints, то высота view тоже изменится.
Теперь рассмотрим любопытный пример расчета вертикальных constraints:
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/view_A"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="View A"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/view_B"
android:text="View B"
android:layout_width="wrap_content"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/view_A"
app:layout_constraintBottom_toTopOf="@id/view_A"/>
<TextView
android:id="@+id/view_C"
android:text="View C"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toRightOf="@id/view_A"
app:layout_constraintTop_toBottomOf="@id/view_B"/>
</android.support.constraint.ConstraintLayout>
Результат:

View A просто привязан к левой и верхней сторонам ConstraintLayout, то есть находится слева сверху.
View B привязан странным образом — Top(B)->bottom(A) и Bottom(B)->top(A), — расстояние между его вертикальными constraints, фактически, отрицательное. Сама высота B выставлена в match_constraint_spread.
View C находится справа от A — Left(C)toRight(A) — и (вроде как) снизу от B — Top(C)toBottom(B).
По горизонтальному расположению вопросов возникнуть не должно. Теперь объясню вертикальное расположение.
Последовательность вертикальных расчетов:
C необходимо рассчитать ее нижний constraint;C необходимо рассчитать верхнюю сторону B;B необходимо рассчитать ее нижний и верхний constraints;B необходимо рассчитать верхнюю и нижнюю стороны A;A просто располагается слева-сверху, размеры рассчитывает сам.Результаты вертикальных расчетов:
A находится на уровне верхней стороны ConstraintLayout, нижняя рассчитывается по размеру текста A, так как у A высота wrap_content;B на уровне нижней стороны A, нижний привязан к верхней стороне A, то есть он на уровне верхней стороны ConstraintLayout;B — match_constraint_spread, то верхняя сторона B — на уровне нижней стороны A, а нижняя — на уровне верхней стороны ConstraintLayout. Это странно, но, фактически, высота B — отрицательная.C привязан к нижней стороне B, то есть он на уровне верхней стороны ConstraintLayout;C на уровне верхней стороны ConstraintLayout, нижняя рассчитывается по размеру текста C, так как у A высота wrap_content.В общем, на мой взгляд, такой алгоритм расчета стоит учитывать, чтобы понимать, где будет располагаться view в конечном счете.
View, привязанная по Baseline, не может быть ограничена сверху и снизу, то есть Top и Bottom constraints будут игнорироваться. Это значит, что для такой view нельзя выставить размер match_constraint_spread или match_constraint_wrap.
Из этого не совсем очевидно следует, что по Baseline стоит привязывать невысокие view к высоким. Иначе есть шанс, что высокая view выйдет за рамки ConstraintLayout или размер ConstraintLayout будет рассчитан неверно.
Пример некорректной Baseline-привязки:
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- Размер текста 12sp -->
<TextView
android:id="@+id/left_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Left view"
android:textSize="12sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<!-- Размер текста 20sp,
то есть больший по размерам элемент привязан по базовой линии к меньшему -->
<TextView
android:id="@+id/right_view"
android:text="Right view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
app:layout_constraintBaseline_toBaselineOf="@id/left_view"
app:layout_constraintLeft_toRightOf="@id/left_view"/>
</android.support.constraint.ConstraintLayout>
Результат:

Высота ConstraintLayout (в черной рамке) равна высоте большой TextView (в красной рамке), так как высота TextView выставлена, как wrap_content.
Базовая линия большой TextView привязана к базовой линии малой TextView (в зеленой рамке), так что текст находится на одной линии.
При этом большая TextView выходит за рамки ConstraintLayout.
При привязке сторон есть одно интересное правило — если привязать две стороны двух элементов друг к другу Left(B)toRight(A) и Right(A)toLeft(B), то элементы будут выделены в цепь и к ним будут применяться особые правила расположения.
Цепью считается набор элементов, стороны которых привязаны друг к другу. Цепи определяются автоматически на основе привязок элементов внутри ConstraintLayout. Цепь располагается на основе привязок ее крайних элементов, а элементы внутри цепи располагаются по правилам определенного стиля цепи. Стиль цепи задается атрибутом layout_constraint{X}_chainStyle, где X — Horizontal для горизонтальных цепей или Vertical для вертикальных.
Пример цепи: правило Right(A)toLeft(B) + Left(B)toRight(A) свяжет элементы A и B в цепь, а Left(A)toLeft(parent) + Right(B)toRight(parent) привяжет всю цепь элементов к внешним сторонам ConstraintLayout.
Стиль цепи и его параметры берутся из атрибутов головного элемента цепи — самого левого, начального или самого верхнего.
Элементы цепи распределяются равномерно, то есть отступы между элементами и от элементов до границ цепи будут одинаковые. Используется по умолчанию;
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- По умолчанию chainStyle — spread, так что значения атрибута можно не указывать -->
<TextView
android:id="@+id/view_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="view_1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/view_2"/>
<TextView
android:id="@+id/view_2"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="view_2"
app:layout_constraintLeft_toRightOf="@id/view_1"
app:layout_constraintRight_toLeftOf="@+id/view_3"/>
<TextView
android:id="@+id/view_3"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:text="view_3"
app:layout_constraintLeft_toRightOf="@id/view_2"
app:layout_constraintRight_toRightOf="parent"/>
</android.support.constraint.ConstraintLayout>

Элементы цепи распределяются так же, как и при стиле spread, но отступы от границ цепи всегда равны нулю;
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- ChainStyle головного элемента — spread_inside -->
<TextView
android:id="@+id/view_1"
app:layout_constraintHorizontal_chainStyle="spread_inside"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="view_1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/view_2"/>
<TextView
android:id="@+id/view_2"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="view_2"
app:layout_constraintLeft_toRightOf="@id/view_1"
app:layout_constraintRight_toLeftOf="@+id/view_3"/>
<TextView
android:id="@+id/view_3"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:text="view_3"
app:layout_constraintLeft_toRightOf="@id/view_2"
app:layout_constraintRight_toRightOf="parent"/>
</android.support.constraint.ConstraintLayout>

Элементы располагаются группой друг за другом. Такой стиль позволяет устанавливать относительную позицию группы элементов в доступном цепи пространстве через атрибут layout_constraint{*}_bias. Bias атрибут нужно указывать у головного элемента цепи;
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- ChainStyle головного элемента — packed, bias — 0.3 (30% отступ слева/от начала) -->
<TextView
android:id="@+id/view_1"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintHorizontal_bias="0.3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="view_1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/view_2"/>
<TextView
android:id="@+id/view_2"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="view_2"
app:layout_constraintLeft_toRightOf="@id/view_1"
app:layout_constraintRight_toLeftOf="@+id/view_3"/>
<TextView
android:id="@+id/view_3"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:text="view_3"
app:layout_constraintLeft_toRightOf="@id/view_2"
app:layout_constraintRight_toRightOf="parent"/>
</android.support.constraint.ConstraintLayout>


Элементы располагаются в соответствии с их весом по аналогии с тем, как работает LinearLayout. Чтобы такой стиль заработал, одна из view цепи должна иметь размер match_constraint_spread. Для указания веса элемента используются атрибуты layout_constraintHorizontal_weight и layout_constraintVertical_weight.
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Ширина match_constraint_spread, weight=3,
по ширине займет 3/4 свободного пространства -->
<TextView
android:id="@+id/view_1"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintHorizontal_weight="3"
android:text="view_1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/view_2"/>
<!-- Ширина — match_constraint_spread, weight=1,
по ширине займет 1/4 свободного пространства -->
<TextView
android:id="@+id/view_2"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintHorizontal_weight="1"
android:text="view_2"
app:layout_constraintLeft_toRightOf="@id/view_1"
app:layout_constraintRight_toLeftOf="@+id/view_3"/>
<!-- Ширина — any_size, займет столько пространства, сколько ей нужно для отрисовки -->
<TextView
android:id="@+id/view_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="view_3"
app:layout_constraintLeft_toRightOf="@id/view_2"
app:layout_constraintRight_toRightOf="parent"/>
</android.support.constraint.ConstraintLayout>

Отступы указываются стандартными атрибутами layout_margin{X}, где X — сторона отступа (Left/Right/Top/Bottom/Start/End). Эти отступы применяются к линиям привязки элемента, а не к сторонам элемента. Это значит, что линия привязки будет с отступом от стороны, к которой она привязана.
Отдельные правила были введены для скрытых элементов, то есть элементов, у которых Visibility выставлено в значение GONE. Когда элемент скрыт, обычные отступы от его сторон игнорируются, но используются специальные gone-отступы. Его размеры при этом при расчетах считаются равными нулю. Gone-отступы представлены атрибутами: layout_goneMargin{X}, где X — сторона отступа.
Другими словами, допустим, у элемента A выставлен отступ слева от элемента B равный 10dp, а gone-отступ слева выставлен 50dp. Если элемент B скрыт (GONE), то отступ элемента A слева будет 50dp, если элемент B — VISIBLE или INVISIBLE, то отступ элемента A слева будет 10dp.
Пример:
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/view_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
android:text="view_1"
app:layout_constraintLeft_toLeftOf="parent"/>
<!-- Левый constraint view_2 привязан к правой стороне view_1.
Так как view_1 скрыта (gone), то используется отступ goneMarginLeft (50dp). -->
<TextView
android:id="@+id/view_2"
android:layout_marginLeft="10dp"
app:layout_goneMarginLeft="50dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="view_2"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintLeft_toRightOf="@id/view_1"
app:layout_constraintRight_toRightOf="parent"/>
</android.support.constraint.ConstraintLayout>

Guideline — это аналог линии, устанавливаемой на макетах в визуальных редакторах, по которой дизайнеры выравнивают элементы. Такую линию представляет view класса android.support.constraint.Guideline. Guideline может быть горизонтальным или вертикальным — это указывается атрибутом android:orientation. Сам guideline нулевого размера, не занимает места в контейнере и всегда привязан только к сторонам ConstraintLayout.
Guideline используется, чтобы привязывать к нему стороны view выравнивая их тем самым по одной линии.
Расположение guideline контролируется тремя атрибутами:
layout_constraintGuide_begin — отступ от левой (Left, а не Start) стороны ConstraintLayout для вертикальных gudeline и от верхней стороны — для горизонтальных;layout_constraintGuide_end — отступ от правой (Right, а не End) стороны ConstraintLayout для вертикальных guideline и от нижней стороны — для горизонтальных;layout_constraintGuide_percent — относительный отступ guideline в процентах от левой стороны ConstraintLayout для вертикальных gudeline и от верхней стороны — для горизонтальных. Указывается числом от 0 до 1. <android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Вертикальный guideline с относительным отступом 25% -->
<android.support.constraint.Guideline
android:id="@+id/line_1"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.25"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<!-- Вертикальный guideline с отступом 100dp от левой стороны ConstraintLayout -->
<android.support.constraint.Guideline
android:id="@+id/line_2"
android:orientation="vertical"
app:layout_constraintGuide_begin="100dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<!-- Вертикальный guideline с отступом 50dp от правой стороны ConstraintLayout -->
<android.support.constraint.Guideline
android:id="@+id/line_3"
android:orientation="vertical"
app:layout_constraintGuide_end="50dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/view_1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="view_1"
app:layout_constraintLeft_toLeftOf="@id/line_1"
app:layout_constraintRight_toRightOf="parent"/>
<TextView
android:id="@+id/view_2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="view_2"
app:layout_constraintTop_toBottomOf="@id/view_1"
app:layout_constraintLeft_toLeftOf="@id/line_1"
app:layout_constraintRight_toRightOf="@id/line_3"/>
<TextView
android:id="@+id/view_3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="view_3"
app:layout_constraintTop_toBottomOf="@id/view_2"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="@id/line_2"/>
</android.support.constraint.ConstraintLayout>

За это отвечает отдельный класс — android.support.constraint.ConstraintSet.
Создать этот ConstraintSet можно тремя способами:
ConstraintLayout;
ConstraintLayout constraintLayout = LayoutInflater.from(context).inflate(R.layout.my_constraint_layout, null);
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(constraintLayout);
*.xml файла, на основе которого создается ConstraintLayout;
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(context, R.layout.my_constraint_layout);
ConstraintSet.
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(constraintSet2);
Описание методов для изменения ConstraintSet можно посмотреть в документации [19] (их очень много).
Чтобы применить ConstraintSet к ConstraintLayout используйте метод applyTo.
final ConstraintLayout constraintLayout = (ConstraintLayout) findViewById(R.id.constr);
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(constraintLayout);
constraintSet.setHorizontalBias(R.id.sample_view, 0.5f);
constraintSet.applyTo(constraintLayout);
Естественно, также можно напрямую изменять LayoutParams конкретного элемента и затем явно вызывать requestLayout. Класс, представляющий все параметры расположения элемента в ConstraintLayout — это android.support.constraint.ConstraintLayout.LayoutParams.
Для анимаций в ConstraintLayout не предусмотрено специальных методов. ConstraintLayout наследуется от ViewGroup, так что для анимации расположения элементов используются те же инструменты, что и для обычных контейнеров: ValueAnimator, TransitionManager и так далее.
Основные преимущества:
ConstraintLayout и не пользоваться в дальнейшем такими контейнерами, как: FrameLayout, LinearLayout, RelativeLayout, GridLayout, PercentRelativeLayout;Основные недостатки:
В компании Touch Instinct мы собираемся, как минимум, попробовать этот компонент. Особенно интересно посмотреть, как он поведет себя при использовании в ячейках RecyclerView — там часто изменяются значения элементов, что обычно приводит к пересчету расположения элементов в ячейке и иногда пересчету ее размера.
Разработчики Google проделали большую работу и у ConstraintLayout есть все шансы стать стандартным при разработке элементом UI, какими стали те же RecyclerView и CoordinatorLayout.
Автор: ZuZuK
Источник [20]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/android-development/253135
Ссылки в тексте:
[1] на прошлом Google I/O: https://www.youtube.com/watch?v=sO9aX87hq9c
[2] 1. Добавляем в проект: https://habrahabr.ru/company/touchinstinct/blog/326814/#dobavlyaem-v-proekt
[3] 2. Contsraints. Привязываем элементы друг к другу: https://habrahabr.ru/company/touchinstinct/blog/326814/#constraints-privyazyvaem-elementy-drug-k-drugu
[4] 3. Задаем размеры View: https://habrahabr.ru/company/touchinstinct/blog/326814/#zadaem-razmery-view
[5] 4. Задаем размеры View на основе соотношения сторон: https://habrahabr.ru/company/touchinstinct/blog/326814/#zadaem-razmery-view-na-osnove-sootnosheniya-storon
[6] 5. Выставляем относительное расположение View внутри ConstraintLayout: https://habrahabr.ru/company/touchinstinct/blog/326814/#vystavlyaem-otnositelnoe-raspolozhenie-view-vnutri-constraintlayout
[7] 6. Особенности привязки линий: https://habrahabr.ru/company/touchinstinct/blog/326814/#osobennosti-privyazki-liniy
[8] 7. Особенности привязки по Baseline: https://habrahabr.ru/company/touchinstinct/blog/326814/#osobennosti-privyazki-po-baseline
[9] 8. Создаем цепи (chains): https://habrahabr.ru/company/touchinstinct/blog/326814/#sozdaem-cepi-chains
[10] 8.1. Стиль spread: https://habrahabr.ru/company/touchinstinct/blog/326814/#stil-spread
[11] 8.2. Стиль spread_inside: https://habrahabr.ru/company/touchinstinct/blog/326814/#stil-spread_inside
[12] 8.3. Стиль packed: https://habrahabr.ru/company/touchinstinct/blog/326814/#stil-packed
[13] 8.4. Стиль weighted: https://habrahabr.ru/company/touchinstinct/blog/326814/#stil-weighted
[14] 9. Указываем отступы с учетом скрытых элементов: https://habrahabr.ru/company/touchinstinct/blog/326814/#ukazyvaem-otstupy-s-uchetom-skrytyh-elementov
[15] 10. Разбираемся с Guidelines: https://habrahabr.ru/company/touchinstinct/blog/326814/#razbiraemsya-s-guidelines
[16] 11. Настраиваем параметры ConstraintLayout из кода: https://habrahabr.ru/company/touchinstinct/blog/326814/#nastraivaem-parametry-constraintlayout-iz-koda
[17] 12. Коротко об анимации: https://habrahabr.ru/company/touchinstinct/blog/326814/#korotko-ob-animacii
[18] 13. Стоит ли пользоваться ConstraintLayout?: https://habrahabr.ru/company/touchinstinct/blog/326814/#stoit-li-polzovatsya-constraintlayout
[19] документации: https://developer.android.com/reference/android/support/constraint/ConstraintSet.html
[20] Источник: https://habrahabr.ru/post/326814/
Нажмите здесь для печати.