Новый макет ConstraintLayout появился в Android Studio 2.2 и доступен для устройств с версии Android 2.3. Его можно найти в разделе Layouts. Гугл очень расхваливает данный макет и советует всем переходить на него и даже создал специальный конвертер для этой задачи. Если у вас имеется старый проект, то достаточно щёлкнуть правой кнопкой мыши на корневом элемента макета и выбрать пункт Convert layout to ConstraintLayout. В диалоговом окне ничего не трогаем.

В build.gradle модуля прописывается ссылка на библиотеку и проект начинает синхронизироваться. Сейчас уже активно развивается ветка 2.х.x, лучше сразу переходить на неё.
В Android Studio 2.3 и выше в шаблонах по умолчанию теперь используется ConstraintLayout.
Если в режиме дизайна выбран данный компонент, то на панели инструментов вам доступны несколько кнопок.
- View Options с пунктами Show Constraints — выводит заданные ограничения в режимах предварительного просмотра и раскладки. В одних случаях этот режим просмотра полезен, в других нет. При большом количестве ограничений эта кнопка выводит слишком много информации, Show Margins, Fade Unselected views.
- Turn On Autoconnect — при включении режима Autoconnect ограничения будут автоматически настраиваться при перетаскивании представлений в область предварительного просмотра. Студия старается угадать, какие ограничения должен иметь компонент, и создавать их по мере необходимости
- Default Margins — стандартное значение для отступов. Можете устанавливать отдельно для каждого компонента. Выбрали компонент, установили значение, затем снова выбрали другой компонент и установили другое значение
- Clear All Constraints — удаляет все ограничения из макета
- Infer Constraints — автоматически создаёт ограничения. Срабатывает только при нажатии кнопки. Функциональность Autoconnect срабатывает каждый раз, когда в файл макета добавляется новый компонент
- GuideLines с двумя опциями: Add Vertical GuideLine и Add Horizontal GuideLine. Смотри ниже
Очень часто на форумах встречается вопрос, почему в режиме дизайна макет выглядит хорошо, а при запуске приложения все компоненты сбиваются в верхний левый угол. Для решения этой проблемы попробуйте нажать на кнопку Infer Constraints, которая создаст дополнительные ограничения.
ConstraintLayout является наследником ViewGroup и местами похож на RelativeLayout, но более продвинут. Код разметки в XML-представлении:
Управление компонентами внутри данного контейнера достаточно сильно отличается от старого взаимодействия. Придётся всем переучиваться.
При его использовании нет смысла использовать XML-представление, только в режиме Design, когда вы можете подвигать все компоненты в визуальном редакторе.
Рассмотрим отдельные элементы, которые используются для редактирования макета. Переключитесь в режим Blueprint, чтобы ничего нас не отвлекало от работы.

Так выглядит выбранный компонент.

Квадратные опорные точки в углах компонента позволяют изменять его размеры. Круглые опорные точки по краям позволяют управлять отступами от краёв экрана и других компонентов.
Продолговатый закругленный прямоугольник указывает на базовую линию текста. Пригодится при выравнивании по базовой линии другого компонента.

Также можно увидеть зигзагообразные направляющие-пружинки, которые нужны для особых случаев. Если провести мышкой над опорной точкой пружинки, она красиво активируется. Последующий щелчок удаляет пружинку. Если компонент удерживался двумя пружинками сверху и снизу, то удаление одной из них притянет компонент к верхней или нижней части экрана. Лучше самостоятельно проверить у себя, так как словами трудно описать.
Пружинки также можно легко восстановить, если щёлкнуть по круглой опорной точке и потянуть её к краю экрана.
Наверху на панели инструментов можно активировать автоматический режим размещения компонентов Autoconnect в виде магнита
. В этом случае вы просто бросаете на экран нужный компонент, а далее студия сама пытается найти нужные параметры для него. Всё это происходит с красивой анимацией. Естественно, вы можете всё переиграть и поставить в нужную позицию.
На той же панели есть инструмент Infern Constraints в виде пары звёздочек
. Действует как Autoconnect, только работает не с одним компонентом, редактируемым в данный момент, а со всем макетом сразу, используя математические расчёты, чтобы определить, какие компоненты нужно привязать к другим, исходя из их местоположения на экране.
Теперь рассмотрим настройки в панели Properties.

Набор из трёх стрелок
внутри квадрата означают атрибут wrap_content. Если вы измените этот атрибут с помощью выпадающего списка или вручную напишите размер в dp, то увидите, что стрелки заменятся на прямые (фиксированный размер
) или пружинку (match_constraints
, который является приблизительным аналогом атрибута match_parent и имеет значение 0dp).
По бокам квадрата имеются числа. Если подвести к ним мышку, то появится выпадающий список с определёнными значениями: 0, 8, 16, 24, 32. Они отвечают за атрибут margin (отступы).

По бокам можно увидеть также ползунки с числами в кружочке. В примере виден только горизонтальный ползунок, при других настройках увидите и вертикальный. С их помощью можно центрировать компонент относительно экрана и других объектов.
Содержание
Aspect ration (Соотношение сторон)
Если у компонента есть двусторонняя вертикальная привязка и значение высоты установлено в match_constraints (0dp), то можно настроить так, чтобы высота зависела от ширины. В углу появится треугольник, щёлкнув на котором можно затем установить желаемое соотношение. Потом можно изменять ширину, чтобы увидеть, как высота подстраивается под ширину.
Аналогично можно настроить зависимость ширины от высоты, предварительно сначала установив двустороннюю горизонтальную привязку.

GuideLine
На панели инструментов также имеется значок GuideLines с двумя опциями: Add Vertical GuideLine и Add Horizontal GuideLine. Если ими воспользоваться, то в XML-файле появятся такие строчки:
По сути, это View, размер которого 0, что соответствует View.GONE. На этапе разработки мы видим только полоски, а во время работы приложения ничего не видим. Данные элементы помогают разместить компоненты аккуратно относительно линии.
Направляющие пригодятся, если одни и те же значения отступов повторяются для нескольких компонентов. Направляющие можно указывать в dp от края экрана или задать в процентах от ширины экрана. Чтобы переключаться между разными режимами, вы можете нажать на круглый значок Guideline.
Не всегда с помощью визуального редактора можно добиться нужного результата, тогда нужно переключиться в XML-режим. Один из таких случаев описан в статье Square Island: Constraint Layout: Icon Label Text.
Если есть желание работать через XML, то следует запомнить очень много атрибутов, например, для выравнивания относительно друг друга:
Атрибут app:layout_constraintHorizontal_bias используется float-значения от 0 до 1, для выравнивания по оси.
Атрибут app:layout_constraintDimensionRatio="4:3" указывает, что нужно использовать данные пропорции по высоте и ширине для данного компонента. Также встречается модификация атрибута app:layout_constraintDimensionRatio="H, 1:1".
Chains — Скованные одной цепью
Несколько элементов можно сковать одной цепью. Допустим, у нас есть три кнопки. Выделяем их и через контекстное меню выбираем Center Horizontally. Снизу у выбранных компонентов появится символ цепи, а между ними будут нарисована связь в виде цепей. Если последовательно щёлкать по значку цепи, то увидите, как кнопки будут центрироваться с разными стилями:
- spread — Свободное пространство равномерно распределяется между выбранными компонентами и краями родителя (например, экрана)
- spread_inside — Крайние компоненты прижимаются к границам родителя, свободное пространство равномерно распределяется только между остальными компонентами
- packed — Свободное пространство равномерно распределяется между крайними компонентами и границами родителя. Вы можете использовать margin для отступов
За цепи отвечают стили.
- app:layout_constraintHorizontal_chainStyle="spread"
- app:layout_constraintVertical_chainStyle="spread"
- app:layout_constraintHorizontal_chainStyle="spread_ins
Также можно присвоить кнопкам вес, когда одна кнопка может быть больше остальных, это поведение знакомо по LinearLayout. Для этих целей используются атрибуты
Как и в LinearLayout, чтобы использовать вес, надо поставить размер компонента в 0dp.
На рисунке этот вариант представлен в третьем примере.

Проценты
Можно указывать значения ширины и высоты в процентах через атрибуты layout_constraintWidth_percent, layout_constraintHeight_percent. Все View-компоненты поддерживают данные атрибуты. Они позволяют ограничить компонент процентным значением в рамках всего доступного пространства. Например, мы хотим видеть кнопку, которая будет занимать 70% в рамках свободного для неё места.
Barriers
Barriers — это виртуальный View, который используется как шаблон. Он применяется для нескольких компонентов неизвестного размера – если один из них увеличивается, то барьер подстроит размер остальных под наибольшую высоту или ширину. Барьеры могут быть вертикальными и горизонтальными и создаваться сверху, снизу, слева или справа от нужных элементов. Другие элементы будут подстраиваться.
Барьеры полезны, когда вы создаёте локализованные строки или отображаете контент, созданный пользователем, размер которого вы не можете предсказать.
Groups
Groups — теперь можно логически группировать определённые виды. По сути, это некий контейнер, который содержит ссылки на ID компонентов, а не сами компоненты. При помощи группы вы можете установить видимость всех компонентов в контейнере. Это может пригодиться, когда сразу несколько элементов должны изменять свою видимость или другие свойства.
Анимация
Для анимации разметки ConstraintLayout используется ConstraintSet.
Circular
С помощью Circular мы можем настроить два компонента так, чтобы одно находилось на определённом расстоянии и под определённым углом от другого.

При создании круговых зависимостей углы начинаются сверху и изменяются по часовой стрелке. Например, таким образом нужно описать среднюю кнопку в примере выше:
Такой способ пригодится для анимации аналоговых часов или похожих примеров.
Placeholder
Элемент Placeholder позволяет использовать место на экране в качестве временного заполнителя. Используя анимацию, можно динамически перемещать компонент на место заполнителя.
ConstraintLayoutStates (2.0.0)
В ветке 2.х появилась новая функциональность — переключение между макетами экрана. Это удобно, когда требуются небольшие изменения на одном экране. Вы должны создать несколько макет с одинаковыми компонентами, но при этом можете изменить их свойства (видимость, местоположение и т.д). В нужный момент программно переключаетесь на нужный макет.
Создадим три разных макета в папке layout.
res/layout/activity_cl_states_start.xml
res/layout/activity_cl_states_loading.xml
res/layout/activity_cl_states_end.xml
В нашем примере во всех макетах имеются ProgressBar и Button с разной видимостью и позицией.
Следующий шаг — создать в папке res/xml новый файл, описывающий три созданных макета.
res/xml/constraint_layout_states.xml
Осталось написать программную часть. Сначала мы загружаем первый макет в стандартном методе setContentView(). Затем загружаем описания созданных нами макетов через loadLayoutDescription() объекта-контейнера нашего ConstraintLayout. Теперь мы можем переключаться между макетами через constraintLayout.setState().
В примере при нажатии кнопки мы имитируем долгоиграющую задачу и выводим ProgressBar, когда задача будет завершена, то выводим третий макет, где кнопка находится уже в другом месте.
Другие атрибуты
Стоит обратить внимание на следующие атрибуты. Они часто используются, если в контейнере содержится компонент RecyclerView.
Дополнительные материалы
Библиотека постоянно развивается. Некоторые пункты меню были переработаны и изменены. Следите за изменениями.
Android Studio по умолчанию предлагает нам использовать ConstraintLayout при создании разметки экрана. Давайте разберемся, что это за штука и как с ней работать.
Хоть это и 180-й урок, но он будет заточен под новичков, т.к. сюда будет вести ссылка из самых первых уроков. Поэтому, опытных разработчиков прошу не удивляться стилю изложения материала, начало вы можете пропустить.
Начнем с самых азов. Чтобы вы могли размещать на экране различные компоненты (кнопки, поля ввода, чекбоксы и т.п.), необходимо использовать специальный контейнер. Именно в него вы будете помещать компоненты. В Android компоненты называются View, а контейнер — ViewGroup.
Существуют несколько типов ViewGroup: LinearLayout, RelativeLayout, FrameLayout, TableLayout, ConstraintLayout и т.д.
Они различаются тем, как они будут упорядочивать компоненты внутри себя. LinearLayout, например, выстроит их по горизонтальной или вертикальной линии. А TableLayout — в виде таблицы. Более подробно об этом вы можете прочитать в Уроке 6.
В этом уроке мы будет разбираться, как будут вести себя компоненты в контейнере ConstraintLayout.
Вообще, слово Constraint переводится как ограничение, принуждение. Но как по мне, так это не совсем точно отражает смысл. Самое подходящее слово, которое я могу тут подобрать — это привязка. Его и буду использовать.
Практика
Чтобы вам самим попрактиковаться, рекомендую создать модуль для этого урока. О том, как создать модуль, мы уже говорили в Уроке 3.
В меню студии: File > New > New module

Application/Library name: ConstraintLayoutIntro
Module name: p0180constraintlayoutintro
Package name: ru.startandroid.p0180constraintlayoutintro
Итак, у нас в Android Studio есть модуль. А в нем есть файл res > layout > activity_main.xml.

Откроем этот файл двойным кликом. Он выглядит так

Убедитесь, что слева-внизу открытка вкладка Design, и режим просмотра выбран Design, а не Blueprint.
Сейчас на экране вы видите текст Hello World. Этот текст отображен с помощью View, которое называется TextView.
Вы можете видеть его в Component Tree (слева-снизу).

Обратите внимание, что TextView как бы вложен в ConstraintLayout. Это то, о чем я говорил в начале. ConstraintLayout — контейнер, а внутри него находятся различные View, в нашем случае — TextView. Также, можно сказать, что ConstraintLayout является родителем или родительским ViewGroup для TextView.
Давайте удалим TextView с экрана. Для этого просто выделите его на экране или в Component Tree и нажмите кнопку Del на клавиатуре.
Теперь ConstraintLayout пуст и экран ничего не отображает.

Если вы вдруг что-то удалили по ошибке, вы всегда можете восстановить это, нажав Ctrl+Z.
А если вы там совсем чего-то наворотили и не получается все это отменить, то откройте вкладку Text (слева-снизу) и вставьте туда этот код:
Ваш экран вернется к первоначальному состоянию.
Зачем нужны привязки
Давайте добавим на экран какой-нибудь компонент, например, снова TextView. Для этого просто перетащите компонент мышкой из Palette на экран.

После этого TextView появился на экране и в Component Tree.
Запустим приложение и посмотрим, как этот текст будет выглядеть.

Видим, что TextView уехал влево и вверх. Что-то явно пошло не так.
Если вы откроете текстовое представление вашего экрана (вкладка Text слева-снизу), то увидите, что элемент TextView подчеркнут красной линией.

Если навести на него мышкой, то он покажет ошибку:
This view is not constrained, it only has designtime positions, so it will jump to (0,0) unless you add constraints.
Этим сообщением студия говорит нам, что View не привязано. Его текущее положение на экране актуально только для разработки (т.е. только в студии). А при работе приложения, это положение будет проигнорировано, и View уедет в точку (0,0), т.е. влево-вверх (что мы и наблюдали при запуске).
Как сделать так, чтобы View в ConstraintLayout оставалось на месте и не смещалось в угол? Необходимо добавить привязки (constraints). Они будут задавать положение View на экране относительно каких-либо других элементов или относительно родительского View.
Как добавить привязки
Давайте добавим привязки для нашего TextView.
Если вы выделите на экране TextView, то можете видеть 4 круга по его бокам.
Эти круги используются, чтобы создавать привязки.
Существует два типа привязок: одни задают положение View по горизонтали, а другие — по вертикали.
Создадим горизонтальную привязку. Привяжем положение TextView к левому краю его родителя. Напомню, что родителем TextView является ConstraintLayout, который в нашем случае занимает весь экран. Поэтому края ConstraintLayout совпадают с краями экрана.
Чтобы создать привязку, нажмите мышкой на TextView, чтобы выделить его. Затем зажмите левой кнопкой мыши левый кружок и тащите его к левой границе.

TextView также уехал влево. Он привязался к левой границе своего родителя.
Но вовсе необязательно они должны быть вплотную. Мы можем задать отступ. Для этого просто зажмите левой кнопкой мыши TextView, перетащите вправо и отпустите.

Обратите внимание на число, которое меняется. Это величина отступа TextView от объекта, к которому он привязан (в нашем случае — от левой границы родителя).

Раньше у нас TextView уезжал влево-вверх, а теперь он уехал только вверх. Влево он не уехал, т.к. мы создали для него горизонтальную привязку. И TextView теперь знает, что по горизонтали он должен располагаться с определенным отступом от левого края.
Давайте создадим вертикальную привязку, чтобы закрепить TextView и по вертикали.

Используем верхний кружок и тащим его к верхней границе. TextView привязывается по вертикали к верхней границе родителя. После этого можно перетащить TextView куда вам нужно, чтобы настроить горизонтальный и вертикальный отступы. При перетаскивании вы видите значения отступов.
Теперь TextView привязан и по горизонтали, и по вертикали. Т.е. он точно знает, где он должен находиться на экране во время работы приложения.
Запускаем, чтобы проверить
TextView никуда не уехал, а находится там, где мы и настроили с помощью привязок.
Давайте добавим еще одно View, например, кнопку — Button.

Если сейчас запустить приложение, то кнопка уедет влево-вверх потому что она ни к чему не привязана.

Мы можем привязывать не только к границам родителя, но и к другим View. Давайте привяжем кнопку к TextView.

Мы привязали кнопку к TextView, создав две привязки

1) Вертикальная привязка. Верхняя граница кнопки привязана к нижней границе TextView. Отступ = 82.
Т.е. можно сказать, что по вертикальной оси:
верхняя граница кнопки = нижняя граница TextView + 82
2) Горизонтальная привязка. Левая граница кнопки привязана к правой границе TextView. Отступ 103.
По горизонтальной оси:
левая граница кнопки = правая граница TextView + 103
Т.к. кнопка привязана к TextView, то, если мы сейчас будем перемещать TextView, то кнопка будет также перемещаться.

Добавим еще View. Например, CheckBox.

Давайте сделаем так, чтобы он по горизонтали находился на том же уровне, что и TextView. Для этого нам надо левую границу CheckBox привязать к левой границе TextView и сделать нулевой отступ. А по вертикали привяжем к нижней границе родителя.

Теперь чекбокс и TextView выравнены по левому краю.
Как удалить привязку
Чтобы удалить привязку, надо просто нажать на соответствующий кружок. Удалим привязки у кнопки.

Чтобы удалить сразу все привязки, есть специальная кнопка

Привязка с двух сторон
Мы рассмотрели примеры, когда View было привязано по каждой оси только с одной стороны. Т.е. только слева или справа по горизонтали, и сверху или снизу по вертикали. Но мы можем привязать View с обоих сторон по каждой оси.
Пока рассмотрим только горизонтальную привязку. Но, разумеется, все это будет работать и для вертикальной привязки.
Давайте попробуем, например, левый край привязать к левой границе родителя, а правый край — к правой границе родителя.
Очистим экран от всех View и добавим новый TextView без каких-либо привязок. Теперь привяжем его к левой и правой границам родителя.

TextView сначала ушел влево, т.к. была привязка к левой границе, но после создания привязки к правой границе он выровнялся и теперь расположен по центру. Т.е. привязки уравняли друг друга, и View находится ровно посередине между тем, к чему он привязан слева, и тем, к чему он привязан справа. Т.е. в нашем случае View находится посередине между левой и правой границами его родителя.
Обратите внимание, что такие двусторонние привязки отображаются как пружинки, а не линии.
Давайте немного усложним пример, добавив кнопку и перенастроив привязки

Кнопка привязана к правому краю. А TextView привязан к левому краю и к кнопке.
Если мы теперь будем перемещать кнопку, то TextView будет оставаться ровно посередине между левым краем и кнопкой.

Мы можем настроить двустороннюю привязку так, чтобы View располагалось не посередине, а ближе к левому краю или к кнопке. Для этого удобно использовать специальный скролл в Properties.

Этим скроллом вы задаете пропорцию. По умолчанию значение = 50. Это половина от 100. Соответственно, View находится на половине расстояния между объектами, к которому оно привязано. В нашем случае, при значении = 50, TextView находится посередине между левым краем и кнопкой.
Если поставить значение, например, 25, то TextView будет находится от левого края на расстоянии равном четверти расстояния между левым краем и кнопкой. Если поставить 75, то TextView будет находится от левого края на расстоянии равном 3/4 от расстояния между левым краем и кнопкой.
И как бы не менялось расстояние между левым краем и кнопкой, эти пропорции всегда будут соблюдаться.
В следующем уроке продолжим рассматривать возможности ConstraintLayout.
P.S.
Если вы пришли в этот урок по ссылке из первых уроков, то теперь вы можете вернуться и продолжить обучение. Этой информации вам будет достаточно.
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Не могу поменять расположение элементов на разметке. Делаю так: 
Получается так: 
То есть, все остальные кнопки скрываются под первой. Подскажите пожалуйста, что я делаю не так?

2 ответа 2
В вашей разметке размещены виджеты, но не установлены связи между ними и краями экрана.
Для того, чтобы в ConstraintLayout расположить три кнопки равномерно по горизонтали нужно воспользоваться инструментом этого контейнера — chains (цепочки). Для этого:
- Переносим на разметку наши кнопки с приблизительным расположением, затем привязываем их к вехнему краю — кликаем на каждую кнопку поочереди и протягиваем верхнюю точку (она станет зеленой) до верхнего края. Установится связь, обозначенная стрелкой:

- Выделяем все три кнопки (Shift + левый клик по кнопке). Затем в верхнем меню пиктограмм нажимаем на две серые горизонтальные черточки с синей вертикальной чертой слева (пиктограмма Align) и там нажимаем пиктограмму с вертикальной чертой и двумя синими стрелками в сторону друг друга (пиктограмма Center Horizontally):

Появится горизонтальная связь — цепочка и добавятся иконки цепи под виджетами. Все, теперь на любых экранах эти три кнопки будут отцентрированы по горизонтали.
Кликая на значок цепи под любым виджетом можно изменять их горизонтальное взаиморасположение (вместе, равномерно, по сторонам).
Подробнее о инструменте Chains в ConstrainLayout и больше вариантов взаиморасположения соединяемых виджетов и их индивидуальных размерах и пропроциях.
Официальная документация по вопросу.
PS: пункт Center Horizontally так же есть в контекстном меню при правом клике на виджете.
«>





