Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do margins in chains work in ConstraintLayout 1.1.0 (beta)

I have had a couple of my layouts blow up since changing over to ConstraintLayout version 1.1.0-beta4. Before I make any changes, I want to get a better understanding of how margins work in ConstraintLayout chains. In the following, I compare a layout in ConstraintLayout version 1.0.2 to version 1.1.0-beta4, but I believe that the issue first arose in 1.1.0-beta2.

My goal is to have some text views stretch across the screen with gaps between the 1st and 2nd text views and the 2nd and 3rd text views. The background should show in these margins. To do this, I create a horizontal chain and specify an end margin from the left text view to the center text view and an end margin from the center text view to the right text view. The horizontal chain style is spread_inside.

Example 1 - Using ConstraintLayout version 1.0.2

This is how things look in version 1.0.2 and is what I expect.

enter image description here

<android.support.constraint.ConstraintLayout 
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:background="@android:color/holo_blue_light">

    <TextView
        android:id="@+id/tvLeft"
        android:layout_width="0dp"
        android:layout_height="35dp"
        android:layout_marginEnd="8dp"
        android:background="@android:color/white"
        android:gravity="center"
        android:text="Text1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/tvCenter"
        app:layout_constraintHorizontal_chainStyle="spread_inside"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="HardcodedText" />

    <TextView
        android:id="@+id/tvCenter"
        android:layout_width="0dp"
        android:layout_height="35dp"
        android:layout_marginEnd="8dp"
        android:background="@android:color/white"
        android:gravity="center"
        android:text="Text2"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/tvRight"
        app:layout_constraintStart_toEndOf="@+id/tvLeft"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="HardcodedText" />

    <TextView
        android:id="@+id/tvRight"
        android:layout_width="0dp"
        android:layout_height="35dp"
        android:background="@android:color/white"
        android:gravity="center"
        android:text="Text3"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/tvCenter"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="HardcodedText" />

</android.support.constraint.ConstraintLayout>

Example 2 - Using ConstraintLayout version 1.1.0-beta4

This same layout looks like the following in version 1.1.0-beta4 of ConstraintLayout. Notice that the margins have disappeared. I expect that this should look the same as example 1, but it doesn't.

enter image description here

Example 3 - Using ConstraintLayout version 1.1.0-beta4 with start margin

If I take this same layout and simply add a start margin of 8dp to the right text view (tvRight), my margins reappear not only between the center and right text views but also between the left and center textviews although I have not changed the margins there.

[image]

This is more than just the previously set margins suddenly being honored. If I set the start margin on the rightmost text view to '48dp', what appears to be a 48dp margin also appears between the left and center text views.

enter image description here

<android.support.constraint.ConstraintLayout 
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:background="@android:color/holo_blue_light">

<!-- TextViews tvLeft & tvRight not shown but are the same as above.-->

<TextView
    android:id="@+id/tvRight"
    android:layout_width="0dp"
    android:layout_height="35dp"
    android:layout_marginStart="48dp"
    android:background="@android:color/white"
    android:gravity="center"
    android:text="Text3"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toEndOf="@+id/tvCenter"
    app:layout_constraintTop_toTopOf="parent"
    tools:ignore="HardcodedText" />

</android.support.constraint.ConstraintLayout>  

So, my question is, "Why am I seeing these results?" How are margins handled in ConstraintLayout chains, especially spread_inside chains? Has there been a change in the way chain margins are handled, or am I missing something? I am looking for an explanation or a reference to some documentation that explains all this.

like image 834
Cheticamp Avatar asked Dec 21 '17 19:12

Cheticamp


People also ask

How do you use chains in constraint layout?

Creating a chain is really easy, we can click and drag to select views or press ctrl and select views from Component Tree. And then right click on selected views in Editor Or Component Tree to apply constraints. You can see that in action in following screen records.

What is constraint layout bias?

Bias, in terms of ConstraintLayout , means "if there is extra room, slide the widget in this direction along the axis". The default bias is 0.5, meaning that the widget is centered in the available space.

What is the use of chains in constraint layout?

Chains In ConstraintLayout: Chains allow us to control the space between elements and chains all the selected elements to another. To create a chain, select the elements that you want to form part of the chain, and then right click – “Chain” – “Create Horizontal or Vertical Chain”.

What are the new features of constraintlayout?

This blog post aims to cover some of the new features, namely Guidelines, Barriers, Chains and Groups. Some of these features require using Android Studio 3.0 Beta 5 and the beta version of ConstraintLayout.

What are chains in Android layouts?

What are chains? Chains are a specific kind of constraint which allow us to share space between the views within the chain and control how the available space is divided between them. The closest analogue with traditional Android layouts is weights in LinearLayout, but chains do far more than that, as we shall see.

How do I use the beta version of constraintlayout?

In order to use the beta version of ConstraintLayout, make sure you have at least the following dependency in your app level build.gradle file: implementation 'com.android.support.constraint:constraint-layout:1.1.0-beta1' Guidelines are small visual helpers to design layouts with.


2 Answers

I can find no documentation that gives an authoritative answer to this exact question. However, there is a little bit of discussion about margins in the API documentation for ConstraintLayout:

If side margins are set, they will be applied to the corresponding constraints (if they exist)

In the specific instance of a chain, you have two-way constraints between each view. That is, not only is View A's end constrained to View B's start, but View B's start is also constrained to View A's end.

In your posted layout, View A has an end constraint and an end margin, but View B has a start constraint with no start margin. As far as I can tell, this means you have conflicting rules in your layout (View A wants to be 8dp away from View B, but View B wants to be 0dp from View A). Perhaps different versions of the ConstraintLayout library have different strategies for (a) determining whether this even counts as a conflict and (b) resolving the conflict if so.

Via experimentation, here is how I've found margins to work in chains on different ConstraintLayout library versions:

Version 1.0.2

Side margins on each view in the chain don't depend on or affect other views in the chain. This has (at least) two visible effects on behavior. First, adding margin to one view will push the other view away by that amount, regardless of that view's margins. Second, adding margin to one view will not affect margins of views farther down the chain (e.g. putting 8dp end margin on your first view does not by itself also cause 8dp worth of space to appear between your second and third views).

Version 1.1.0-beta4

Side margins on each view in the chain both depend on and affect other views in the chain. Again, this has two visible effects on behavior. First, adding margin to one view will not push the other view away unless it also has a margin of that same amount*. Second, adding margin between the first and second view of the chain will also affect the spacing between the second and third view of the chain**.

*: It seems that 1.1.0-beta4 allows just a start margin to push the views apart, while just an end margin will have no effect. Regardless, I recommend matching the margins.

**: I suspect this is because the chain is trying to allocate "space" evenly. The margins between views A and B create a gap, and since the chain wants to enforce a consistent spacing it adds a similar gap between views B and C.

Examples:

Stripped way down, here's a layout just like your original, with the margins changed slightly. I've left every other attribute unchanged.

<android.support.constraint.ConstraintLayout>

    <TextView
        android:layout_marginEnd="8dp"/>

    <TextView
        android:layout_marginStart="8dp"/>

    <TextView/>

</android.support.constraint.ConstraintLayout>

v1.0.2:

enter image description here

v1.1.0-beta4:

enter image description here

This should illustrate the two differences between the library versions. Again, I've been completely unable to find official documentation that explains all this, but it appears to be true just based on experimentation.

like image 136
Ben P. Avatar answered Oct 29 '22 06:10

Ben P.


Expanding on Ben P.'s answer, I have determined the following regarding margins in ConstraintLayout chains. This information applies to ConstraintLayout version 1.1.0-beta4.

General Observations

  1. Within a chain, all start margins (android:layout_marginStart) are honored. This means that the spacing between the views will not be less than the specified start margin. However, the spacing may be greater as explained below.

  2. End margins (android:layout_marginEnd) have no relevance and seem to be ignored. This does not apply to the end margins of the view at the end of the chain but only to the interior margins where views are cross-linked to create the chain.

  3. When a chain is centered within its constraints, the chain is centered between the start margin of the chain's head and the end margin of the chain's tail.

In the examples below, views "A","D" ang "G" are constrained to the parent start. Views "C", "F" and "I" are constrained to the parent end.

Chain Style: packed

If the chain style is "packed," all views are placed end-to-end separated by the specified start margins. The spacing between the views can vary according to how the start margins are defined. In the following image, the width of the views are match_constraints and the margins are set as indicated.

enter image description here

If the widths of the views are set to something other than match_constraints, the views are still packed with the specified margins but the chain is centered between the start margin of the chain's head and the end margin of the chain's tail.

enter image description here

I came to this interpretation instead of considering the end margin to be attached to the end view because the Android Studio designer has this same interpretation:

enter image description here

Chain Style: spread

In the "spread" chain style, all views are distributed between the start and end constraints such that the space before and after each view is the same and equal to the greatest start margin defined. If the width of each view is match_constraints, then all the views will have the same width by default.

Chain Style: spread_inside

The spread_inside style of chains will take the first view of the chain and anchor it to its start constraint while honoring its start margin. The end view will be anchored to its end constraint while honoring its end margin. Interior views will be distributed with equal spacing between the views like spread chains.

enter image description here


Below is the same layout with various margins set. Views "F" and "I" have a start margin of 8dp set but the gap has expanded to 16dp. View "G", "H" and "I" are all of equal width although they don't appear to be.

The XML for this layout is presented at the end of this post.

enter image description here

Of interest but of no real importance: The different chain types are indistinguishable if the views have a width of match_constraints and all margins are zero.

enter image description here

The information above also applies to vertical chains. Substitute android:layout_marginTop for android:layout_marginStart and android:layout_marginBottom for android:layout_marginEnd.

Layout

<android.support.constraint.ConstraintLayout 
    android:id="@+id/constraintLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/holo_blue_light">

    <TextView
        android:id="@+id/heading1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="packed, match_constraints"
        android:textAppearance="@style/TextAppearance.AppCompat.Medium"
        android:textSize="16sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="HardcodedText" />

    <TextView
        android:id="@+id/textA"
        android:layout_width="0dp"
        android:layout_height="35dp"
        android:layout_marginTop="8dp"
        android:background="@android:color/white"
        android:gravity="center"
        android:text="A"
        android:textColor="@android:color/black"
        app:layout_constraintEnd_toStartOf="@+id/textB"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/heading1"
        tools:ignore="HardcodedText" />

    <View
        android:layout_width="8dp"
        android:layout_height="35dp"
        android:background="#ff00cc"
        app:layout_constraintEnd_toStartOf="@id/textB"
        app:layout_constraintTop_toTopOf="@id/textB" />

    <TextView
        android:id="@+id/textB"
        android:layout_width="0dp"
        android:layout_height="35dp"
        android:layout_marginStart="8dp"
        android:background="@android:color/darker_gray"
        android:gravity="center"
        android:text="B"
        android:textColor="@android:color/white"
        app:layout_constraintEnd_toStartOf="@+id/textC"
        app:layout_constraintStart_toEndOf="@+id/textA"
        app:layout_constraintTop_toTopOf="@+id/textA"
        tools:ignore="HardcodedText" />

    <View
        android:id="@+id/view16dpOnC"
        android:layout_width="16dp"
        android:layout_height="35dp"
        android:background="#fffb00"
        app:layout_constraintEnd_toStartOf="@id/textC"
        app:layout_constraintTop_toTopOf="@+id/textC" />

    <TextView
        android:id="@+id/textC"
        android:layout_width="0dp"
        android:layout_height="35dp"
        android:layout_marginStart="16dp"
        android:background="@android:color/white"
        android:gravity="center"
        android:text="C"
        android:textColor="@android:color/black"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/textB"
        app:layout_constraintTop_toTopOf="@+id/textA"
        tools:ignore="HardcodedText" />

    <TextView
        android:id="@+id/heading2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="spread, match_constraints"
        android:textAppearance="@style/TextAppearance.AppCompat.Medium"
        android:textSize="16sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/textA"
        tools:ignore="HardcodedText" />

    <View
        android:layout_width="16dp"
        android:layout_height="35dp"
        android:background="#00ff19"
        app:layout_constraintEnd_toStartOf="@id/textD"
        app:layout_constraintTop_toTopOf="@id/textD" />

    <TextView
        android:id="@+id/textD"
        android:layout_width="0dp"
        android:layout_height="35dp"
        android:layout_marginTop="8dp"
        android:background="@android:color/white"
        android:gravity="center"
        android:text="D"
        android:textColor="@android:color/black"
        app:layout_constraintEnd_toStartOf="@+id/textE"
        app:layout_constraintHorizontal_chainStyle="spread"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/heading2"
        tools:ignore="HardcodedText" />

    <View
        android:layout_width="16dp"
        android:layout_height="35dp"
        android:background="#fffb00"
        app:layout_constraintEnd_toStartOf="@id/textE"
        app:layout_constraintTop_toTopOf="@id/textE" />

    <TextView
        android:id="@+id/textE"
        android:layout_width="0dp"
        android:layout_height="35dp"
        android:layout_marginStart="16dp"
        android:background="@android:color/darker_gray"
        android:gravity="center"
        android:text="E"
        android:textColor="@android:color/white"
        app:layout_constraintEnd_toStartOf="@+id/textF"
        app:layout_constraintStart_toEndOf="@+id/textD"
        app:layout_constraintTop_toTopOf="@+id/textD"
        tools:ignore="HardcodedText" />

    <View
        android:layout_width="8dp"
        android:layout_height="35dp"
        android:background="#003cff"
        app:layout_constraintStart_toEndOf="@id/textE"
        app:layout_constraintTop_toTopOf="@+id/textE" />

    <View
        android:layout_width="8dp"
        android:layout_height="35dp"
        android:background="#ff00cc"
        app:layout_constraintEnd_toStartOf="@id/textF"
        app:layout_constraintTop_toTopOf="@id/textF" />

    <TextView
        android:id="@+id/textF"
        android:layout_width="0dp"
        android:layout_height="35dp"
        android:layout_marginStart="8dp"
        android:background="@android:color/white"
        android:gravity="center"
        android:text="F"
        android:textColor="@android:color/black"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/textE"
        app:layout_constraintTop_toTopOf="@+id/textD"
        tools:ignore="HardcodedText" />

    <View
        android:layout_width="16dp"
        android:layout_height="35dp"
        android:background="#00ff19"
        app:layout_constraintStart_toEndOf="@id/textF"
        app:layout_constraintTop_toTopOf="@id/textF" />

    <TextView
        android:id="@+id/heading3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="spread_inside, match_constraints"
        android:textAppearance="@style/TextAppearance.AppCompat.Medium"
        android:textSize="16sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/textD"
        tools:ignore="HardcodedText" />

    <View
        android:layout_width="8dp"
        android:layout_height="35dp"
        android:background="#003cff"
        app:layout_constraintEnd_toStartOf="@id/textG"
        app:layout_constraintTop_toTopOf="@+id/textG" />

    <TextView
        android:id="@+id/textG"
        android:layout_width="0dp"
        android:layout_height="35dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:background="@android:color/white"
        android:gravity="center"
        android:text="G"
        android:textColor="@android:color/black"
        app:layout_constraintEnd_toStartOf="@+id/textH"
        app:layout_constraintHorizontal_chainStyle="spread_inside"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/heading3"
        tools:ignore="HardcodedText" />

    <View
        android:layout_width="16dp"
        android:layout_height="35dp"
        android:background="#fffb00"
        app:layout_constraintEnd_toStartOf="@id/textH"
        app:layout_constraintTop_toTopOf="@id/textH" />

    <TextView
        android:id="@+id/textH"
        android:layout_width="0dp"
        android:layout_height="35dp"
        android:layout_marginStart="16dp"
        android:background="@android:color/darker_gray"
        android:gravity="center"
        android:text="H"
        android:textColor="@android:color/white"
        app:layout_constraintEnd_toStartOf="@+id/textI"
        app:layout_constraintStart_toEndOf="@+id/textG"
        app:layout_constraintTop_toTopOf="@+id/textG"
        tools:ignore="HardcodedText" />

    <View
        android:layout_width="8dp"
        android:layout_height="35dp"
        android:background="#003cff"
        app:layout_constraintStart_toEndOf="@id/textH"
        app:layout_constraintTop_toTopOf="@id/textH" />

    <View
        android:layout_width="8dp"
        android:layout_height="35dp"
        android:background="#ff00cc"
        app:layout_constraintEnd_toStartOf="@id/textI"
        app:layout_constraintTop_toTopOf="@id/textI" />

    <TextView
        android:id="@+id/textI"
        android:layout_width="0dp"
        android:layout_height="35dp"
        android:layout_marginStart="8dp"
        android:background="@android:color/white"
        android:gravity="center"
        android:text="I"
        android:textColor="@android:color/black"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/textH"
        app:layout_constraintTop_toTopOf="@+id/textG"
        tools:ignore="HardcodedText" />

    <View
        android:layout_width="8dp"
        android:layout_height="35dp"
        android:background="#ff00cc"
        android:visibility="gone"
        app:layout_constraintEnd_toStartOf="@id/textC"
        app:layout_constraintTop_toTopOf="@id/textC" />

    <View
        android:id="@+id/view8dp"
        android:layout_width="8dp"
        android:layout_height="35dp"
        android:layout_marginStart="24dp"
        android:background="#ff00cc"
        app:layout_constraintBottom_toTopOf="@id/view8dpGap"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textG"
        app:layout_constraintVertical_bias="0.100000024"
        app:layout_constraintVertical_chainStyle="packed" />

    <TextView
        android:id="@+id/text8dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:text="8dp start margin"
        app:layout_constraintBottom_toBottomOf="@+id/view8dp"
        app:layout_constraintStart_toEndOf="@id/view8dp"
        app:layout_constraintTop_toTopOf="@+id/view8dp"
        tools:ignore="HardcodedText" />

    <View
        android:id="@+id/view8dpGap"
        android:layout_width="8dp"
        android:layout_height="35dp"
        android:layout_marginStart="24dp"
        android:layout_marginTop="8dp"
        android:background="#003cff"
        app:layout_constraintBottom_toTopOf="@+id/view16dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/view8dp" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:text="8dp gap not defined by start margin"
        app:layout_constraintBottom_toBottomOf="@+id/view8dpGap"
        app:layout_constraintStart_toEndOf="@+id/view8dpGap"
        app:layout_constraintTop_toTopOf="@+id/view8dpGap"
        tools:ignore="HardcodedText" />

    <View
        android:id="@+id/view16dp"
        android:layout_width="17dp"
        android:layout_height="35dp"
        android:layout_marginStart="24dp"
        android:layout_marginTop="8dp"
        android:background="#fffb00"
        app:layout_constraintBottom_toTopOf="@+id/view16dpGap"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/view8dpGap" />

    <TextView
        android:id="@+id/text16dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:text="16dp start margin"
        app:layout_constraintBottom_toBottomOf="@+id/view16dp"
        app:layout_constraintStart_toEndOf="@+id/view16dp"
        app:layout_constraintTop_toTopOf="@+id/view16dp"
        tools:ignore="HardcodedText" />

    <View
        android:id="@+id/view16dpGap"
        android:layout_width="17dp"
        android:layout_height="35dp"
        android:layout_marginStart="24dp"
        android:layout_marginTop="8dp"
        android:background="#00ff19"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/view16dp" />

    <TextView
        android:id="@+id/text16dpGap"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:text="16dp gap not defined by start margin"
        app:layout_constraintBottom_toBottomOf="@+id/view16dpGap"
        app:layout_constraintStart_toEndOf="@+id/view16dpGap"
        app:layout_constraintTop_toTopOf="@+id/view16dpGap"
        tools:ignore="HardcodedText" />

</android.support.constraint.ConstraintLayout>
like image 40
Cheticamp Avatar answered Oct 29 '22 06:10

Cheticamp