ConstraintLayout: cambiar las restricciones mediante programación

109

Necesito ayuda con ConstraintSet. Mi objetivo es cambiar las restricciones de la vista en el código, pero no puedo averiguar cómo hacerlo bien.

Tengo 4 TextViewsy uno ImageView. Necesito establecer ImageViewrestricciones a una de las 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);

Si la primera respuesta es correcta, necesito establecer restricciones de ImageViewa

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

Si la segunda respuesta es correcta, necesito establecer restricciones de ImageViewa

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

Y así.

Gran entrenador
fuente
para esto, debe cambiar la restricción dinámicamente.
Shweta Chauhan
3
@shweta Estoy preguntando exactamente sobre esto, ¿cómo hacerlo dinámicamente?
Big Coach
consiguiendo. publicando su respuesta.
Shweta Chauhan

Respuestas:

184
  1. Para establecer las limitaciones de la vista de imágenes a:

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

    utilizar:

     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 establecer las limitaciones de la vista de imágenes a:

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

    utilizar:

     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
fuente
3
constricciónSet.clone (constricciónLayout); en esta línea, ¿es constraintLayout el diseño principal?
Reejesh PK
5
@Pang .clone(constraintLayout)¿Qué es esta variable y de dónde la obtengo?
leonheess
8
@ReejeshPK @ MiXT4PE Sí, ES el diseño principal, es decirConstraintLayout constraintLayout = findViewById(R.id.parent_layout);
Muhammad Muzammil
1
@leonheess Creo que debería ser una variable que haga referencia a su grupo de vistas de diseño de restricciones
Felix Favor Chinemerem
81

Supongamos que queremos cambiar las restricciones durante el tiempo de ejecución, haciendo que button1 se alinee con button2 cuando se hace clic:

ingrese la descripción de la imagen aquí

Entonces, teniendo este diseño:

<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 hacer lo siguiente:


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

azizbeko
fuente
Amigo mío, no entiendo tu código ... ¿qué es layoutParamsy val? ¿Es esto incluso Java?
leonheess
42
Señor, es el lenguaje de programación Kotlin. El equivalente de Java seráConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) button1.getLayoutParams();
azizbekian
1
Creo que olvidaste escribir button1.layoutParams = params
sumit sonawane
1
@sumitsonawane, no es necesario, porque estamos mutando esa instancia y después de eso realizamos lo button1.requestLayout()que luego inspeccionará la instancia de la LayoutParamsque hemos mutado.
azizbeko
2
@azizbekian, sí, para mí la solución final es reemplazar la requestLayout()llamada con setLayoutParams()y luego funciona. Simplemente mutar layoutParamsy solicitar el diseño por mí mismo no parece funcionar.
qwertyfinger
2

En Kotlin, simplemente puede extender la ConstraintSetclase y agregar algunos métodos para aprovechar dsl en Kotlin y producir un código más legible. Me gusta esto

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
}

Y úsalo así

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

Sé que mi respuesta es muy tardía, pero estoy seguro de que ayudaría mucho a otros que pasen por aquí. Este artículo no es mío, pero hice algunos cambios, dicho esto, deberías esforzarte por consultar el artículo completo aquí.

Conjuntos de restricciones

La clave para trabajar con conjuntos de restricciones en código Java es la clase ConstraintSet. Esta clase contiene una variedad de métodos que permiten tareas como crear, configurar y aplicar restricciones a una instancia de ConstraintLayout. Además, las restricciones actuales para una instancia de ConstraintLayout pueden copiarse en un objeto ConstraintSet y usarse para aplicar las mismas restricciones a otros diseños (con o sin modificaciones).

Se crea una instancia de ConstraintSet como cualquier otro objeto Java:

ConstraintSet set = new ConstraintSet();

Una vez que se ha creado un conjunto de restricciones, se pueden llamar métodos en la instancia para realizar una amplia gama de tareas. El siguiente código configura un conjunto de restricciones en el que el lado izquierdo de una vista de botón está conectado al lado derecho de una vista de EditText con un margen de 70 dp:

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

Aplicación de restricciones a un diseño Una vez configurado el conjunto de restricciones, debe aplicarse a una instancia de ConstraintLayout antes de que surta efecto. Un conjunto de restricciones se aplica mediante una llamada al método applyTo (), pasando a través de una referencia al objeto de diseño al que se aplicarán las configuraciones:

set.applyTo(myLayout);

Hay muchas más cosas que puede hacer con la ConstraintSetAPI, configurar el sesgo horizontal y vertical, centrar horizontal y verticalmente, manipular cadenas y mucho más.

Muy buena lectura.

Nuevamente, esto es solo una adaptación.

Felix Favor Chinemerem
fuente
0

Además de la respuesta de Azizbekian , permítanme señalar dos cosas:

  1. Si la izquierda / derecha no funcionó, intente comenzar / finalizar así:

params.startToEnd = button2.id

  1. Si desea eliminar una restricción, use el indicador UNSET como este:

params.startToEnd = ConstraintLayout.LayoutParams.UNSET

Oleg Yablokov
fuente