ConstraintLayout: программно изменять ограничения

111

Мне нужна помощь ConstraintSet. Моя цель - изменить ограничения представления в коде, но я не могу понять, как это сделать правильно.

У меня 4 TextViewс и один ImageView. Мне нужно установить ImageViewограничения для одного из TextViews.

check_answer4 = (TextView) findViewById(R.id.check_answer4);
check_answer1 = (TextView) findViewById(R.id.check_answer1);
check_answer2 = (TextView) findViewById(R.id.check_answer2);
check_answer3 = (TextView) findViewById(R.id.check_answer3);

correct_answer_icon = (ImageView) findViewById(R.id.correct_answer_icon);

Если 1-й ответ правильный, мне нужно установить ограничения ImageViewдо

app:layout_constraintRight_toRightOf="@+id/check_answer1"
app:layout_constraintTop_toTopOf="@+id/check_answer1"

Если 2-й ответ правильный, мне нужно установить ограничения ImageViewна

app:layout_constraintRight_toRightOf="@+id/check_answer2"
app:layout_constraintTop_toTopOf="@+id/check_answer2"

И так далее.

Большой тренер
источник
для этого необходимо динамически изменять ограничение.
Shweta Chauhan
4
@shweta Я именно об этом и спрашиваю, как это сделать динамически?
Big Coach
получение. размещая свой ответ.
Shweta Chauhan

Ответы:

189
  1. Чтобы установить ограничения просмотра изображения:

     app:layout_constraintRight_toRightOf="@+id/check_answer1"
     app:layout_constraintTop_toTopOf="@+id/check_answer1"

    использование:

     ConstraintLayout constraintLayout = findViewById(R.id.parent_layout);
     ConstraintSet constraintSet = new ConstraintSet();
     constraintSet.clone(constraintLayout);
     constraintSet.connect(R.id.imageView,ConstraintSet.RIGHT,R.id.check_answer1,ConstraintSet.RIGHT,0);
     constraintSet.connect(R.id.imageView,ConstraintSet.TOP,R.id.check_answer1,ConstraintSet.TOP,0);
     constraintSet.applyTo(constraintLayout);
  2. Чтобы установить ограничения просмотра изображения:

     app:layout_constraintRight_toRightOf="@+id/check_answer2"
     app:layout_constraintTop_toTopOf="@+id/check_answer2"

    использование:

     ConstraintLayout constraintLayout = findViewById(R.id.parent_layout);
     ConstraintSet constraintSet = new ConstraintSet();
     constraintSet.clone(constraintLayout); 
     constraintSet.connect(R.id.imageView,ConstraintSet.RIGHT,R.id.check_answer2,ConstraintSet.RIGHT,0);      
     constraintSet.connect(R.id.imageView,ConstraintSet.TOP,R.id.check_answer2,ConstraintSet.TOP,0);
     constraintSet.applyTo(constraintLayout);
вишака Ёлекар
источник
3
constraintSet.clone (constraintLayout); в этой строке является constraintLayout родительский макет?
Reejesh PK
5
@Pang .clone(constraintLayout)что это за переменная и откуда ее взять?
leonheess
8
@ReejeshPK @ MiXT4PE Да, это родительский макет, т.е.ConstraintLayout constraintLayout = findViewById(R.id.parent_layout);
Мухаммад Музаммил
1
@leonheess Я думаю, это должна быть переменная, ссылающаяся на вашу группу просмотра макета ограничений
Феликс Фавор Чинемерем
84

Предположим, мы хотим изменить ограничения во время выполнения, заставив button1 выровняться с button2 при нажатии:

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

Затем, имея этот макет:

<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="match_parent"
    android:id="@+id/root"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">


    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button 1"
        app:layout_constraintTop_toTopOf="@+id/button3"
        app:layout_constraintBottom_toBottomOf="@+id/button3"
        app:layout_constraintStart_toEndOf="@+id/button3"
        android:layout_marginStart="0dp"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginEnd="0dp" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="16dp"
        android:text="Button 2"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginStart="8dp"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginEnd="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_marginBottom="8dp"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginTop="8dp"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintVertical_bias="0.5" />

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="16dp"
        android:text="Button 3"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginStart="8dp"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginEnd="8dp"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginTop="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_marginBottom="8dp"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintVertical_bias="0.223" />
</android.support.constraint.ConstraintLayout>

Мы можем сделать следующее:


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        button1.setOnClickListener {
            val params = button1.layoutParams as ConstraintLayout.LayoutParams
            params.leftToRight = button2.id
            params.topToTop = button2.id
            params.bottomToBottom = button2.id
            button1.requestLayout()
        }
    }

азизбекский
источник
Друг мой, я не понимаю твой код .. что это layoutParamsи что val? Это вообще Java?
leonheess
45
Сэр, это язык программирования котлин. Эквивалент Java будетConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) button1.getLayoutParams();
azizbekian
1
Я думаю, вы забыли написать button1.layoutParams = params
sumit sonawane
1
@sumitsonawane, это не нужно, потому что мы мутируем этот экземпляр, а после этого выполняем, button1.requestLayout()который затем проверит экземпляр, LayoutParamsкоторый мы мутировали.
азизбекян
2
@azizbekian, да, для меня окончательное решение - заменить requestLayout()call на, setLayoutParams()и тогда оно работает. Простое изменение layoutParamsи запрос макета, похоже, не помогает.
qwertyfinger
3

В Kotlin вы можете просто расширить ConstraintSetкласс и добавить несколько методов, чтобы воспользоваться преимуществами dsl в Kotlin и создать более читаемый код. Как это

class KotlinConstraintSet : ConstraintSet() {

    companion object {
        inline fun buildConstraintSet(block:KotlinConstraintSet.()->Unit) =
            KotlinConstraintSet().apply(block)
    }
    //add this if you plan on using the margin param in ConstraintSet.connect
    var margin: Int? = null
        get() {
            val result = field
            margin = null //reset it to work with other constraints
            return result
        }

    inline infix fun Unit.and(other: Int) = other // just to join two functions

    inline infix fun Int.topToBottomOf(bottom: Int) =
        margin?.let {
            connect(this, TOP, bottom, BOTTOM, it)
        } ?: connect(this, TOP, bottom, BOTTOM)

    inline fun margin(margin: Int) {
        this.margin = margin
    }

    inline infix fun Int.bottomToBottomOf(bottom: Int) =
        margin?.let {
            connect(this, BOTTOM, bottom, BOTTOM, it)
        } ?: connect(this, BOTTOM, bottom, BOTTOM)

    inline infix fun Int.topToTopOf(top: Int) =
        margin?.let {
            connect(this, TOP, top, TOP, it)
        } ?: connect(this, TOP, top, TOP)

    inline infix fun Int.startToEndOf(end: Int) =
        margin?.let {
            connect(this, START, end, END, it)
        } ?: connect(this, START, end, END)

            ...
    //TODO generate other functions depending on your needs

    infix fun Int.clear(constraint: Constraints) =
        when (constraint) {
            Constraints.TOP -> clear(this, TOP)
            Constraints.BOTTOM -> clear(this, BOTTOM)
            Constraints.END -> clear(this, END)
            Constraints.START -> clear(this, START)
        }

    //inline infix fun clearTopCon
    inline infix fun appliesTo(constraintLayout: ConstraintLayout) =
        applyTo(constraintLayout)

    inline infix fun clones(constraintLayout: ConstraintLayout) =
        clone(constraintLayout)

    inline fun constraint(view: Int, block: Int.() -> Unit) =
        view.apply(block)
}

enum class Constraints {
    TOP, BOTTOM, START, END //you could add other values to use with the clear fun like LEFT
}

И используйте это так

        buildConstraintSet {
            this clones yourConstraintLayout
            constraint(R.id.view1) {
                margin(value:Int) and this topToBottomOf R.id.view2
                margin(30) and this bottomToBottomOf ConstraintSet.PARENT_ID
            }
            constraint(R.id.view2) {
                this clear Constraints.BOTTOM
                margin(0) and this topToTopOf R.id.topGuide
            }
            constraint(R.id.view4) {
                this topToTopOf R.id.view2
                this bottomToBottomOf R.id.view3
                this startToEndOf R.id.view2
            }
            //or you could simply do
            R.id.view1 startToEndOf R.view2
            R.id.view1 toptToBottomOf R.view3
            R.id.view3 bottomtToBottomOf R.view2
            R.id.view3 clear Constraints.END

            // and finally call applyTo()
            this appliesTo yourConstraintLayout
        }
Кофи
источник
2

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

Наборы ограничений

Ключом к работе с наборами ограничений в коде Java является класс ConstraintSet. Этот класс содержит ряд методов, которые позволяют выполнять такие задачи, как создание, настройка и применение ограничений к экземпляру ConstraintLayout. Кроме того, текущие ограничения для экземпляра ConstraintLayout можно скопировать в объект ConstraintSet и использовать для применения тех же ограничений к другим макетам (с изменениями или без них).

Экземпляр ConstraintSet создается так же, как и любой другой объект Java:

ConstraintSet set = new ConstraintSet();

После создания набора ограничений в экземпляре можно вызывать методы для выполнения широкого круга задач. Следующий код настраивает набор ограничений, в котором левая сторона представления кнопки соединяется с правой стороной представления EditText с полем 70dp:

set.connect(button1.getId(), ConstraintSet.LEFT, 
        editText1.getId(), ConstraintSet.RIGHT, 70);

Применение ограничений к макету После того, как набор ограничений настроен, его необходимо применить к экземпляру ConstraintLayout, прежде чем он вступит в силу. Набор ограничений применяется через вызов метода applyTo (), передавая ссылку на объект макета, к которому должны быть применены настройки:

set.applyTo(myLayout);

С помощью ConstraintSetAPI вы можете сделать гораздо больше: установка горизонтального и вертикального смещения, центрирование по горизонтали и вертикали, управление цепочками и многое другое.

Действительно приятно читать.

Опять же, это всего лишь адаптация.

Феликс Фавор Чинемерем
источник
2

В дополнение к ответу азизбекяна позвольте мне указать на две вещи:

  1. Если влево / вправо не сработали, попробуйте начать / закончить так:

params.startToEnd = button2.id

  1. Если вы хотите удалить ограничение, используйте флаг UNSET следующим образом:

params.startToEnd = ConstraintLayout.LayoutParams.UNSET

Олег Яблоков
источник