我需要帮助ConstraintSet。我的目标是在代码中改变视图的约束,但我不知道如何做到这一点。

我有4个textview和一个ImageView。我需要将ImageView约束设置为textview之一。

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

如果第一个答案是正确的,我需要设置ImageView的约束为

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

如果第二个答案是正确的,我需要设置ImageView的约束为

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

等等。


当前回答

在Kotlin中,您可以简单地扩展ConstraintSet类并添加一些方法来利用Kotlin中的dsl并生成更可读的代码。 像这样

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
        }

其他回答

在Kotlin中,您可以简单地扩展ConstraintSet类并添加一些方法来利用Kotlin中的dsl并生成更可读的代码。 像这样

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
        }

@vishakha yeolekar的解决方案对我不起作用。

要更改约束,我们需要遵循以下步骤:

克隆父布局 清除先前的约束 连接约束 将约束应用到父布局

解决方案代码(Kotlin)

val clParent = findViewById<ConstraintLayout>(R.id.parent_layout)
val mConstraintSet = ConstraintSet()
mConstraintSet.clone(clParent)
mConstraintSet.clear(R.id.imageView, ConstraintSet.END)
mConstraintSet.connect(R.id.imageView, ConstraintSet.END, R.id.check_answer, ConstraintSet.END)
mConstraintSet.applyTo(clParent)

这里是ConstraintSet的更多信息和方法的链接-点击这里。

你也可以使用TransitionManager来动画化这些变化:

public static void animateConstraintLayout(ConstraintLayout constraintLayout, ConstraintSet set, long duration) {
    AutoTransition trans = new AutoTransition();
    trans.setDuration(duration);
    trans.setInterpolator(new AccelerateDecelerateInterpolator());

    TransitionManager.beginDelayedTransition(constraintLayout, trans);
    set.applyTo(constraintLayout);
}

你可以在用ConstraintSet更新布局后这样调用它:

ConstraintSet set = new ConstraintSet();
set.clone(constraintLayout);
set.connect(R.id.example, ConstraintSet.BOTTOM, R.id.anotherExample, ConstraintSet.BOTTOM);
set.connect(R.id.example, ConstraintSet.TOP, R.id.anotherExample, ConstraintSet.TOP);

animateConstraintLayout(constraintLayout, set, 500);

你可以用ConstraintSet或ConstraintLayout在ConstraintLayout中创建约束。LayoutParams属性。

为了方便使用,我创建了一个ConstraintSet kotlin扩展。 这是简单而健壮的。

使用

 applyConstraint {
            centerHorizontallyParent(imageView)
            centerHorizontallyParent(textView)
            topToParent(imageView, px(12))
            topToBottom(textView, imageView, px(4))
        }

Code

fun ConstraintLayout.applyConstraint(block: ConstraintSet.() -> Unit) {
    ConstraintSet().apply {
        clone(this@applyConstraint)
        block(this)
    }.applyTo(this)
}

fun ConstraintSet.centerParent(v1: View) {
    centerHorizontallyParent(v1)
    centerVerticallyParent(v1)
}

fun ConstraintSet.centerHorizontallyParent(v1: View) {
    centerHorizontally(v1.id, PARENT_ID)
}

fun ConstraintSet.centerVerticallyParent(v1: View) {
    centerVertically(v1.id, PARENT_ID)
}

fun ConstraintSet.topToTop(v1: View, v2: View, @Px margin: Int = 0) {
    connect(v1.id, TOP, v2.id, TOP, margin)
}

fun ConstraintSet.topToParent(v1: View, @Px margin: Int = 0) {
    connect(v1.id, TOP, PARENT_ID, TOP, margin)
}

fun ConstraintSet.bottomToBottom(v1: View, v2: View, @Px margin: Int = 0) {
    connect(v1.id, BOTTOM, v2.id, BOTTOM, margin)
}

fun ConstraintSet.bottomToParent(v1: View, @Px margin: Int = 0) {
    connect(v1.id, BOTTOM, PARENT_ID, BOTTOM, margin)
}

fun ConstraintSet.topToBottom(v1: View, v2: View, @Px margin: Int = 0) {
    connect(v1.id, TOP, v2.id, BOTTOM, margin)
}

fun ConstraintSet.bottomToTop(v1: View, v2: View, @Px margin: Int = 0) {
    connect(v1.id, BOTTOM, v2.id, TOP, margin)
}

fun ConstraintSet.startToStart(v1: View, v2: View, @Px margin: Int = 0) {
    connect(v1.id, START, v2.id, START, margin)
}

fun ConstraintSet.startToParent(v1: View, @Px margin: Int = 0) {
    connect(v1.id, START, PARENT_ID, START, margin)
}

fun ConstraintSet.endToEnd(v1: View, v2: View, @Px margin: Int = 0) {
    connect(v1.id, END, v2.id, END, margin)
}

fun ConstraintSet.endToParent(v1: View, @Px margin: Int = 0) {
    connect(v1.id, END, PARENT_ID, END, margin)
}

fun ConstraintSet.startToEnd(v1: View, v2: View, @Px margin: Int = 0) {
    connect(v1.id, START, v2.id, END, margin)
}

fun ConstraintSet.endToStart(v1: View, v2: View, @Px margin: Int = 0) {
    connect(v1.id, END, v2.id, START, margin)
}

例如:

app:layout_constraintRight_toRightOf = "parentView"

如果你喜欢用程序声明它,它将是…

ConstraintLayout.LayoutParams.rightToRight = parentView.getId()

如果你使用程序来声明parentview(viewgroup),不要忘记给parentview一个生成的id。

parentView.setId(View.generatedId());