ConstraintLayout: thay đổi các ràng buộc theo chương trình


109

Tôi cần giúp đỡ với ConstraintSet. Mục tiêu của tôi là thay đổi các ràng buộc của chế độ xem trong mã, nhưng tôi không thể tìm ra cách thực hiện điều này đúng.

Tôi có 4 TextViews và một ImageView. Tôi cần thiết lập các ImageViewràng buộc cho một trong các 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);

Nếu câu trả lời đầu tiên là đúng, tôi cần đặt các ràng buộc đối ImageViewvới

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

Nếu câu trả lời thứ 2 là đúng, tôi cần phải hạn chế bộ ImageViewđể

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

Và như thế.


đối với điều này, bạn phải thay đổi ràng buộc động.
Shweta Chauhan

3
@shweta Tôi đang hỏi chính xác về điều này, làm thế nào để thực hiện nó một cách linh động?
Big Coach

nhận được. đăng câu trả lời của bạn.
Shweta Chauhan

Câu trả lời:


184
  1. Để đặt các ràng buộc của chế độ xem hình ảnh thành:

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

    sử dụng:

     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. Để đặt các ràng buộc của chế độ xem hình ảnh thành:

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

    sử dụng:

     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 (bindLayout); trong dòng này, bindLayout có phải là bố cục mẹ không?
Reejesh PK

5
@Pang .clone(constraintLayout)biến này là gì và tôi lấy nó từ đâu?
leonheess

8
@ReejeshPK @ MiXT4PE Có nó là cách bố trí cha mẹ, tức làConstraintLayout constraintLayout = findViewById(R.id.parent_layout);
Muhammad Muzammil

1
@leonheess Tôi nghĩ rằng đó phải là một biến tham chiếu đến Nhóm ViewGroup Bố cục Ràng buộc của bạn
Felix Favor Chinemerem

80

Giả sử chúng ta muốn thay đổi các ràng buộc trong thời gian chạy, làm cho button1 được căn chỉnh với button2 khi được nhấp:

nhập mô tả hình ảnh ở đây

Sau đó, có bố cục này:

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

Chúng tôi có thể làm như sau:


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


Bạn của tôi, tôi không nhận được mã của bạn .. là gì layoutParamsvà là valgì? Đây có phải là Java không?
leonheess

42
Thưa bạn, đó là ngôn ngữ lập trình kotlin. Java tương đương sẽ làConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) button1.getLayoutParams();
azizbekian

1
Tôi nghĩ rằng bạn đã quên viết button1.layoutParams = params
sumit sonawane

1
@sumitsonawane, nó không cần thiết, bởi vì chúng tôi đang thay đổi phiên bản đó và sau đó chúng tôi thực hiện button1.requestLayout(), sau đó chúng tôi sẽ kiểm tra phiên bản LayoutParamsmà chúng tôi đã biến đổi.
azizbekian

2
@azizbekian, vâng, đối với tôi, giải pháp cuối cùng là thay thế requestLayout()cuộc gọi bằng setLayoutParams()và sau đó nó hoạt động. Chỉ đơn giản là tự mình thay đổi layoutParamsvà yêu cầu bố cục dường như không làm được việc.
qwertyfinger

2

Trong Kotlin, bạn có thể chỉ cần mở rộng ConstraintSetlớp và thêm một số phương thức để tận dụng lợi thế của dsl trong Kotlin và tạo ra một mã dễ đọc hơn. Như thế này

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
}

Và sử dụng nó như thế này

        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

Tôi biết câu trả lời của mình là rất muộn, nhưng tôi chắc chắn Nó sẽ giúp ích cho những người ghé qua đây rất nhiều. Bài viết này không phải của tôi nhưng tôi đã thực hiện một vài thay đổi, điều đó đã nói, bạn nên cố gắng xem toàn bộ bài viết tại đây

Bộ ràng buộc

Chìa khóa để làm việc với các bộ ràng buộc trong mã Java là lớp ConstraintSet. Lớp này chứa một loạt các phương thức cho phép các tác vụ như tạo, cấu hình và áp dụng các ràng buộc cho một cá thể ConstraintLayout. Ngoài ra, các ràng buộc hiện tại cho một cá thể ConstraintLayout có thể được sao chép vào một đối tượng ConstraintSet và được sử dụng để áp dụng các ràng buộc tương tự cho các bố cục khác (có hoặc không có sửa đổi).

Một cá thể ConstraintSet được tạo giống như bất kỳ đối tượng Java nào khác:

ConstraintSet set = new ConstraintSet();

Khi một tập hợp ràng buộc đã được tạo, các phương thức có thể được gọi trên phiên bản để thực hiện một loạt các tác vụ. Đoạn mã sau định cấu hình bộ ràng buộc trong đó phía bên trái của chế độ xem Nút được kết nối với phía bên phải của chế độ xem EditText với lề 70dp:

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

Áp dụng các ràng buộc cho một bố cục Sau khi tập hợp ràng buộc được cấu hình, nó phải được áp dụng cho một cá thể ConstraintLayout trước khi nó có hiệu lực. Một tập hợp ràng buộc được áp dụng thông qua lệnh gọi phương thức applyTo (), chuyển qua một tham chiếu đến đối tượng bố cục mà các cài đặt sẽ được áp dụng:

set.applyTo(myLayout);

Có rất nhiều thứ khác bạn có thể làm với ConstraintSet API, Đặt thiên vị ngang và dọc, căn giữa theo chiều ngang và chiều dọc, thao tác Chuỗi và hơn thế nữa.

Đọc thực sự rất hay.

Một lần nữa, đây chỉ là một bản chuyển thể.


0

Ngoài câu trả lời của azizbekian , hãy để tôi chỉ ra hai điều:

  1. Nếu trái / phải không hoạt động, hãy thử bắt đầu / kết thúc như thế này:

params.startToEnd = button2.id

  1. Nếu bạn muốn loại bỏ một ràng buộc, hãy sử dụng cờ UNSET như sau:

params.startToEnd = ConstraintLayout.LayoutParams.UNSET

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.