【问题标题】:ConstraintLayout: change constraints programmaticallyConstraintLayout:以编程方式更改约束
【发布时间】:2017-07-23 08:41:01
【问题描述】:

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

我有 4 个TextViews 和一个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);

如果第一个答案是正确的,我需要将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"

等等。

【问题讨论】:

  • 为此,您必须动态更改约束。
  • @shweta 我在问这个问题,如何动态地做到这一点?
  • 得到。发布您的答案。

标签: android android-layout android-view android-constraintlayout


【解决方案1】:
  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);
    

【讨论】:

  • constraintSet.clone(constraintLayout);在这一行中,constraintLayout 是父布局吗?
  • @Pang .clone(constraintLayout) 这个变量是什么,我从哪里得到它?
  • @ReejeshPK @MiXT4PE 是的,它是父布局,即ConstraintLayout constraintLayout = findViewById(R.id.parent_layout);
  • @leonheess 我认为这应该是一个引用您的约束布局视图组的变量
  • 尝试了 10 种不同的方法,这很有效。谢谢!
【解决方案2】:

假设我们想在运行时改变约束,使 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 吗?
  • 先生,这是 kotlin 编程语言。 Java 等价物将是 ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) button1.getLayoutParams();
  • 我想你忘了写 button1.layoutParams = params
  • @sumitsonawane,这不是必需的,因为我们正在变异该实例,然后我们执行button1.requestLayout(),然后将检查我们已变异的LayoutParams 实例。
  • @azizbekian,是的,对我来说,最终的解决方案是将requestLayout() 调用替换为setLayoutParams(),然后它就可以工作了。简单地改变layoutParams 并自己请求布局似乎并不能解决问题。
【解决方案3】:

另一种方法是像这样更新视图的布局参数(无需请求布局):

yourView.updateLayoutParams<ConstraintLayout.LayoutParams> {
        startToEnd = otherView.id
        topToTop = otherView.id
        bottomToBottom = otherView.id
        //add other constraints if needed
      }

【讨论】:

    【解决方案4】:

    除了azizbekian's answer,我还要指出两点:

    1. 如果左/右不起作用,请尝试像这样开始/结束:

    params.startToEnd = button2.id

    1. 如果要删除约束,请像这样使用 UNSET 标志:

    params.startToEnd = ConstraintLayout.LayoutParams.UNSET

    【讨论】:

    • UNSET 非常有用,可以帮助解决我遇到的问题
    【解决方案5】:

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

    【讨论】:

      【解决方案6】:

      我知道我的回答已经很晚了,但我相信它会帮助到这里的其他人很多。 这篇文章不是我的,但我做了一些改动,话虽如此,你应该努力查看完整的文章here

      约束集

      在 Java 代码中使用约束集的关键是 ConstraintSet 类。此类包含一系列方法,这些方法允许创建、配置和应用约束到 ConstraintLayout 实例等任务。此外,可以将 ConstraintLayout 实例的当前约束复制到 ConstraintSet 对象中,并用于将相同的约束应用于其他布局(修改或不修改)。

      一个 ConstraintSet 实例的创建与任何其他 Java 对象一样:

      ConstraintSet set = new ConstraintSet();
      

      一旦创建了约束集,就可以在实例上调用方法来执行各种任务。 以下代码配置了一个约束集,其中 Button 视图的左侧以 70dp 的边距连接到 EditText 视图的右侧:

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

      对布局应用约束 约束集配置好后,必须应用到一个 ConstraintLayout 实例后才会生效。通过调用 applyTo() 方法来应用约束集,并传递对要应用设置的布局对象的引用:

      set.applyTo(myLayout);
      

      您可以使用ConstraintSet API 执行更多操作、设置水平和垂直偏差、水平和垂直居中、操纵链等等。

      真的很好读。

      同样,这只是一个改编。

      【讨论】:

        【解决方案7】:

        @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 的更多信息和方法的链接 - Click Here。

        【讨论】:

          【解决方案8】:

          您还可以使用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);
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2014-01-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-11-25
            • 1970-01-01
            相关资源
            最近更新 更多