ConstraintLayout: alterar restrições de forma programática

110

Eu preciso de ajuda ConstraintSet. Meu objetivo é alterar as restrições de visualização no código, mas não consigo descobrir como fazer isso direito.

Eu tenho 4 se TextViewum ImageView. Eu preciso definir ImageViewrestrições para um dos 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);

Se a primeira resposta estiver certa, preciso definir as restrições de ImageViewpara

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

Se a 2ª resposta estiver certa, preciso definir restrições de ImageViewpara

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

E assim por diante.

Grande treinador
fonte
para isso, você deve alterar a restrição dinamicamente.
Shweta Chauhan
3
@shweta estou perguntando exatamente sobre isso, como fazer dinamicamente?
Big Coach
obtendo. postando sua resposta.
Shweta Chauhan

Respostas:

185
  1. Para definir as restrições de visualização da imagem para:

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

    usar:

     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. Para definir as restrições de visualização da imagem para:

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

    usar:

     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);
vishakha yeolekar
fonte
3
constraintSet.clone (constraintLayout); nesta linha, constraintLayout é o layout pai?
Reejesh PK
5
@Pang, o .clone(constraintLayout)que é essa variável e de onde a obtenho?
leonheess
8
@ReejeshPK @ MiXT4PE sim, é o layout dos pais, ou seja,ConstraintLayout constraintLayout = findViewById(R.id.parent_layout);
Muhammad Muzammil
1
@leonheess Eu acho que deveria ser uma variável referenciando seu ViewGroup de Layout de Restrição
Felix Favor Chinemerem
81

Suponha que queremos alterar as restrições durante o tempo de execução, fazendo com que o botão1 fique alinhado com o botão2 quando clicado:

insira a descrição da imagem aqui

Então, tendo este layout:

<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>

Podemos fazer o seguinte:


    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()
        }
    }

azizbekian
fonte
Meu amigo, não recebo seu código .. o que é layoutParamse val? Isso é mesmo Java?
leonheess
42
Senhor, é a linguagem de programação kotlin. O equivalente em Java seráConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) button1.getLayoutParams();
azizbekian
1
Acho que você se esqueceu de escrever button1.layoutParams = params
sumit sonawane
1
@sumitsonawane, não é necessário, porque estamos alterando aquela instância e depois disso executamos o button1.requestLayout()que irá inspecionar a instância LayoutParamsdaquilo que alteramos.
azizbekian
2
@azizbekian, sim, para mim a solução final é substituir requestLayout()call por setLayoutParams()e então funciona. Simplesmente mudar layoutParamse solicitar o layout não parece funcionar.
qwertyfinger
2

No Kotlin, você pode simplesmente estender a ConstraintSetclasse e adicionar alguns métodos para aproveitar as vantagens do dsl no Kotlin e produzir um código mais legível. Como isso

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
}

E usar assim

        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
        }
Kofi
fonte
2

Eu sei que minha resposta está muito atrasada, mas tenho certeza que ajudaria muito outras pessoas que passam por aqui. Este artigo não é meu, mas fiz algumas alterações, sendo assim, você deve se esforçar para verificar o artigo completo aqui

Conjuntos de restrições

A chave para trabalhar com conjuntos de restrições no código Java é a classe ConstraintSet. Esta classe contém uma variedade de métodos que permitem tarefas como criar, configurar e aplicar restrições a uma instância de ConstraintLayout. Além disso, as restrições atuais para uma instância de ConstraintLayout podem ser copiadas para um objeto ConstraintSet e usadas para aplicar as mesmas restrições a outros layouts (com ou sem modificações).

Uma instância de ConstraintSet é criada como qualquer outro objeto Java:

ConstraintSet set = new ConstraintSet();

Após a criação de um conjunto de restrições, os métodos podem ser chamados na instância para realizar uma ampla gama de tarefas. O código a seguir configura um conjunto de restrições em que o lado esquerdo de uma visualização de botão é conectado ao lado direito de uma visualização de EditText com uma margem de 70 dp:

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

Aplicando restrições a um layout Depois que o conjunto de restrições é configurado, ele deve ser aplicado a uma instância de ConstraintLayout antes de ter efeito. Um conjunto de restrições é aplicado por meio de uma chamada ao método applyTo (), passando por uma referência ao objeto de layout ao qual as configurações devem ser aplicadas:

set.applyTo(myLayout);

Há muito mais coisas que você pode fazer com a ConstraintSetAPI, definir o viés horizontal e vertical, centralizar horizontalmente e verticalmente, manipular cadeias e muito mais.

Leitura realmente boa.

Novamente, esta é apenas uma adaptação.

Felix Favor Chinemerem
fonte
0

Além da resposta de azizbekian , deixe-me apontar duas coisas:

  1. Se esquerda / direita não funcionou, tente iniciar / terminar assim:

params.startToEnd = button2.id

  1. Se você quiser remover uma restrição, use o sinalizador UNSET como este:

params.startToEnd = ConstraintLayout.LayoutParams.UNSET

Oleg Yablokov
fonte