Равномерное размещение представлений с помощью ConstraintLayout

Обычно LinearLayout используется для равномерного распределения (веса) представлений, например: пример макета

Как реализовать такие равномерно расположенные виды с помощью нового ConstraintLayout?

ConstraintLayout ссылки для справки: сообщение в блоге, Видео сеанса ввода-вывода


person AdamK    schedule 30.05.2016    source источник


Ответы (5)


Это можно сделать двумя способами с помощью ConstraintLayout: Chains. и Рекомендации. Чтобы использовать цепочки, убедитесь, что вы используете ConstraintLayout Beta 3 или новее, и если вы хотите использовать редактор визуального макета в Android Studio, убедитесь, что вы используете Android Studio 2.3 Beta 1 или новее.

Метод 1. Использование цепочек

Откройте редактор макета и добавьте свои виджеты как обычно, добавляя родительские ограничения по мере необходимости. В этом случае я добавил две кнопки с ограничениями внизу родительского элемента и сбоку от родительского элемента (левая сторона для кнопки «Сохранить» и правая сторона для кнопки «Поделиться»):

введите описание изображения здесь

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

введите описание изображения здесь

Выделите оба представления, нажав Ctrl / Cmd или перетащив рамку вокруг представлений:

введите описание изображения здесь

Затем щелкните виды правой кнопкой мыши и выберите «Центрировать по горизонтали»:

введите описание изображения здесь

Это устанавливает двунаправленную связь между представлениями (так определяется цепочка). По умолчанию стиль цепочки - «распространение», который применяется, даже если атрибут XML не включен. Придерживаясь этого стиля цепочки, но установка ширины наших представлений на 0dp позволяет представлениям заполнять доступное пространство, равномерно распределяясь по родительскому элементу:

введите описание изображения здесь

Это более заметно в альбомной ориентации:

введите описание изображения здесь

Если вы предпочитаете пропустить редактор макета, результирующий XML будет выглядеть так:

<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="match_parent">

<Button
    android:id="@+id/button_save"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:text="@string/button_save_text"
    android:layout_marginStart="8dp"
    android:layout_marginBottom="8dp"
    android:layout_marginEnd="4dp"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintRight_toLeftOf="@+id/button_share"
    app:layout_constraintHorizontal_chainStyle="spread" />

<Button
    android:id="@+id/button_share"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:text="@string/button_share_text"
    android:layout_marginStart="4dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"
    app:layout_constraintLeft_toRightOf="@+id/button_save"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintBottom_toBottomOf="parent" />

</android.support.constraint.ConstraintLayout>

Подробности:

  • установка ширины каждого элемента на 0dp или MATCH_CONSTRAINT позволяет представлениям заполнять родительский элемент (необязательно)
  • представления должны быть связаны между собой двунаправленно (справа от ссылок кнопки сохранения на кнопку совместного использования, слева от ссылок кнопки общего доступа к кнопке сохранения), это произойдет автоматически через редактор макета при выборе «По центру по горизонтали»
  • первое представление в цепочке может указывать стиль цепочки через layout_constraintHorizontal_chainStyle, см. документация для различных стилей цепочек, если стиль цепочки опущен, по умолчанию используется "распространение"
  • вес цепи можно отрегулировать с помощью layout_constraintHorizontal_weight
  • этот пример для горизонтальной цепочки, есть соответствующие атрибуты для вертикальных цепочек

Метод 2 - Использование рекомендаций

Откройте свой макет в редакторе и нажмите кнопку с направляющими:

введите описание изображения здесь

Затем выберите «Добавить вертикальную направляющую»:  введите описание изображения здесь

Появится новая направляющая, которая по умолчанию, скорее всего, будет привязана к левой стороне в относительных значениях (обозначена стрелкой, направленной влево):

Относительные рекомендации редактора макета

Щелкните стрелку влево, чтобы установить процентное значение, затем перетащите направляющую до отметки 50%:

процентное соотношение редактора макета

Направляющую теперь можно использовать в качестве привязки для других представлений. В моем примере я прикрепил правую часть кнопки сохранения и левую часть кнопки общего доступа к направляющей:

окончательный макет

Если вы хотите, чтобы представления заполняли доступное пространство, тогда ограничение должно быть установлено на «Любой размер» (волнистые линии, идущие горизонтально):

ограничение любого размера

(Это то же самое, что установить layout_width на 0dp).

Руководство также можно довольно легко создать в XML, а не с помощью редактора макета:

<android.support.constraint.Guideline
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/guideline"
    android:orientation="vertical"
    app:layout_constraintGuide_percent="0.5" />
person AdamK    schedule 30.05.2016
comment
Я не мог найти способ создать руководство с ограничением. Я хочу, чтобы горизонтальная направляющая была посередине двух представлений. Представьте себе увеличенный вид с высотой 100dp вверху и меньший с высотой 50dp внизу. Я хочу разместить направляющую в середине промежутка между ними. - person headsvk; 11.06.2016
comment
Я не думаю, что вы можете добавлять ограничения к самому руководству. Вы можете добавить несколько руководящих принципов, а затем ограничить просмотры этими руководящими принципами. Вы можете задать новый вопрос с подробностями о том, чего вы пытаетесь достичь. Не стесняйтесь снова вставить его сюда. - person AdamK; 12.06.2016
comment
Я хочу придать видам пропорциональную ширину. Например, я хочу, чтобы кнопка «Поделиться» была в два раза шире кнопки сохранения. Без использования рекомендаций, поскольку мои представления не расположены рядом друг с другом, как в этом примере. Является ли это возможным? - person Shubham Naik; 30.11.2017
comment
Вы должны преобразовать значения, указанные в рекомендациях, в фактические поля или отступы. Руководства работают только в режиме дизайна. - person Abhinav Saxena; 25.12.2018
comment
Можем ли мы сделать что-то вроде этого: если я скрою первую кнопку, вторая кнопка автоматически выровняется по центру ..? - person Himanshu; 26.03.2019
comment
Похоже, рекомендации - это лучший способ. Простой. Как вы думаете, какой метод лучше всего подходит для вас? - person portfoliobuilder; 11.07.2019
comment
Просто чтобы добавить к ответу для случая цепочек, пожалуйста, убедитесь, что удалили все поля в том направлении, в котором вы хотите создать цепочку. Например, если вы хотите создать вертикальную цепочку, убедитесь, что все соответствующие представления не имеют верхнего и нижнего полей, иначе опция вертикальной цепочки даже не появится. - person Uncaught Exception; 04.03.2020

Чтобы создать 2 вида в одной строке одинаковой ширины, просто нужно определить

<android.support.constraint.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <Button
        android:id="@+id/button1"
        android:layout_width="0dp"  
        android:layout_height="wrap_content"
        android:text="Button 1"
        app:layout_constraintEnd_toStartOf="@+id/button2"
        app:layout_constraintStart_toStartOf="parent" />

    <Button
        android:id="@+id/button2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Button 2"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/button1" />

</android.support.constraint.ConstraintLayout>

Примечание

  • ширина = 0dp (MATCH_CONSTRAINT)
  • Ограничение button1 и button2 должно быть таким, как указано выше

Результат

ЕЩЕ
Если вы хотите View1 больше, чем View2, вы можете использовать weight или percent.
Пример, View1 width = 2 * View2 width используйте вес

<android.support.constraint.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    >

    <Button
        android:id="@+id/button3"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Button 3"
        app:layout_constraintEnd_toStartOf="@+id/button4"
        app:layout_constraintHorizontal_weight="2"
        app:layout_constraintStart_toStartOf="parent"
        />

    <Button
        android:id="@+id/button4"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Button 4"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintStart_toEndOf="@+id/button3"
        />

</android.support.constraint.ConstraintLayout>

Результат

Например, View1 width = 2 * View2 ширина использовать процент

<android.support.constraint.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    >

    <Button
        android:id="@+id/button5"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Button 5"
        app:layout_constraintEnd_toStartOf="@+id/button6"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintWidth_percent="0.667"
        />

    <Button
        android:id="@+id/button6"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Button 6"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/button5"
        app:layout_constraintWidth_percent="0.333"
        />

</android.support.constraint.ConstraintLayout>

Результат

person Linh    schedule 19.10.2018

Хорошо, если это кому-то поможет

здесь ключ app:layout_constraintHorizontal_weight="1" и
лучшее в макете ограничений то, что он поддерживает циклическую зависимость, и вот что я сделал, используя именно это.

Для первого ребенка
app:layout_constraintEnd_toStartOf="@+id/textInputSecondChild"

Для второго ребенка

app:layout_constraintLeft_toRightOf="@+id/textInputFirstChild"

вот полная демонстрация

<android.support.design.widget.TextInputLayout
    android:id="@+id/textInputParent"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent">

    <EditText
        android:id="@+id/editTextParent"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/state" />
</android.support.design.widget.TextInputLayout>

<android.support.design.widget.TextInputLayout
    android:id="@+id/textInputFirstChild"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintEnd_toStartOf="@+id/textInputSecondChild"
    app:layout_constraintHorizontal_weight="1"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/textInputParent">

    <EditText
        android:id="@+id/editTextChildOne"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/pin_code" />
</android.support.design.widget.TextInputLayout>

<android.support.design.widget.TextInputLayout
    android:id="@+id/textInputSecondChild"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintHorizontal_weight="1"
    app:layout_constraintLeft_toRightOf="@+id/textInputFirstChild"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/textInputParent">

    <EditText
        android:id="@+id/editTextChildSecond"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/country" />
</android.support.design.widget.TextInputLayout>
person rookieDeveloper    schedule 18.03.2017

Вы должны прочитать о цепях с утяжелением. Пример кода здесь.

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    >

    <TextView
        android:id="@+id/figure_1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        app:layout_constraintEnd_toStartOf="@id/figure_2"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintStart_toStartOf="parent"
        tools:text="1"
        />

    <TextView
        android:id="@+id/figure_2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        app:layout_constraintEnd_toStartOf="@id/figure_3"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintStart_toEndOf="@id/figure_1"
        tools:text="2"
        />

    <TextView
        android:id="@+id/figure_3"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        app:layout_constraintEnd_toStartOf="@id/figure_4"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintStart_toEndOf="@id/figure_2"
        tools:text="3"
        />

    <TextView
        android:id="@+id/figure_4"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintStart_toEndOf="@id/figure_3"
        tools:text="4"
        />
</android.support.constraint.ConstraintLayout>

Итак, установите android:layout_width="0dp", app:layout_constraintHorizontal_weight="1" и свяжите каждое представление с соседями, например:

app:layout_constraintStart_toEndOf="@id/figure_2"
app:layout_constraintEnd_toStartOf="@id/figure_4"

введите описание изображения здесь

person CoolMind    schedule 18.07.2018
comment
какой смысл публиковать еще один ответ, точно такой же, как другой, опубликованный два года назад? - person ; 18.07.2018
comment
@Subzero, я видел много раз одинаковые ответы с высокой оценкой. Даже строки кода были такими же. Подозреваю, что некоторые авторы скопировали с моих и даже получили больше плюсов. В этом случае ответы разные, я также использовал другие источники, чтобы понять, как работают веса в ConstraintLayout, и только первого ответа было недостаточно, чтобы получить картину выше. - person CoolMind; 19.07.2018

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

<TextView1
     app:layout_constraintHorizontal_weight="1" />
 <TextView2
     app:layout_constraintHorizontal_weight="1" />
 <TextView3
     app:layout_constraintHorizontal_weight="1" />
 <TextView4
     app:layout_constraintHorizontal_weight="1" />

введите описание изображения здесь

person tzg    schedule 29.10.2019