Trying to replicate GridLayout column alignment with ConstraintLayout
Asked Answered
L

4

25

I'm new to ConstraintLayout, and I'm trying to replicate the same grid behavior offered by GridLayout with ConstraintLayout.

Specifically, I want to design a two columns grid. The first column width should be as narrow as possible, while the second column should take all the remaining horizontal space. Of course, the second column should be just to the right of the first column, or rather, to the widest view on the first column.

I don't know how can I replicate this last requirement with ConstraintLayout. I don't want to use a Gridline between the two columns, because the first column should not have a fixed nor a percentage width, but rather be as wide as the widest of its view.

At https://gist.github.com/venator85/499dd82f47b3efbbed7a1e9e1ca1412d I prepared a layout example, and the corresponding preview, showing a GridLayout that implements what I want. The first two ConstraintLayout attempts in that layout show C1 and D1 be aligned to B1, and C2 and D2 be aligned to B2. When B2 is narrower than A2, A1 and C1 will overlap.

Any help?

Thanks

Lyonnais answered 16/3, 2017 at 22:56 Comment(3)
Are the contents of each textview dynamic, or are they of fixed length?Maricruzmaridel
The content of all textviews is dynamic.Lyonnais
Is there a (good)reason to switch to CoordinatorLayout? Your layout is a grid/table and you already have a good widget to build the layout in the form of GridLayout. I don't think you can replicate the dynamic grid the GridLayout offers by default with a widget which has view based constraints.Key
D
17

Google has introduced the idea of "barriers" in the latest release of ConstraintLayout that helps to make an answer to this question 100% solvable in XML. See the ConstraintLayout 1.1.0 beta 1 release notes . Although, that note doesn't contain a lot of information on the new capabilities there was a talk at I/O 2017 that touched on the new stuff.

The new solution is to replicate the grid of GridLayout with barriers. There is a vertical barrier placed to the right of the left-hand TextViews and a barrier under the top three rows. The barriers shift depending upon how much text is present in each TextView but always maintain the position specified in app:constraint_referenced_ids. In essence, barriers act like floating guidelines. This solution is not relying upon any coding to support what is in the video.

Here is a video of the new layout that shows the desired positioning each TextView being maintained as the contents of another TextView changes. The video was made in the design tools of Android Studio 2.3.2.

And XML for the new layout using barriers:

<?xml version="1.0" encoding="utf-8"?>
<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:id="@+id/constrained"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.constraint.Barrier
        android:id="@+id/barrierVertical"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:orientation="vertical"
        app:barrierDirection="right"
        app:constraint_referenced_ids="L1, L2, L3, L4" />

    <TextView
        android:id="@+id/L1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:text="L1 *"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="HardcodedText" />

    <TextView
        android:id="@+id/R1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:text="R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1*"
        app:layout_constraintLeft_toRightOf="@+id/barrierVertical"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="HardcodedText" />

    <android.support.constraint.Barrier
        android:id="@+id/barrier1"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:orientation="horizontal"
        app:barrierDirection="bottom"
        app:constraint_referenced_ids="L1, R1" />

    <TextView
        android:id="@+id/L2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:text="L2 L2*"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="@id/barrier1"
        tools:ignore="HardcodedText" />

    <TextView
        android:id="@+id/R2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:text="R2 R2 R2 R2 R2 R2 R2 R2 R2 R2 R2 R2 R2 R2 R2 R2 R2 R2 R2 R2 R2 R2 R2*"
        app:layout_constraintLeft_toRightOf="@+id/barrierVertical"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/barrier1"
        tools:ignore="HardcodedText" />

    <android.support.constraint.Barrier
        android:id="@+id/barrier2"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:orientation="horizontal"
        app:barrierDirection="bottom"
        app:constraint_referenced_ids="L2, R2" />

    <TextView
        android:id="@+id/L3"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:text="L3 L3 L3*"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="@id/barrier2"
        tools:ignore="HardcodedText" />

    <TextView
        android:id="@+id/R3"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:text="R3 R3 R3 R3 R3 R3 R3 R3 R3 R3 R3 R3 R3 R3 R3 R3 R3 R3 R3 R3 R3 R3*"
        app:layout_constraintLeft_toRightOf="@id/barrierVertical"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/barrier2"
        tools:ignore="HardcodedText" />

    <android.support.constraint.Barrier
        android:id="@+id/barrier3"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:orientation="horizontal"
        app:barrierDirection="bottom"
        app:constraint_referenced_ids="L3, R3" />

    <TextView
        android:id="@+id/L4"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:text="L4 L4 L4 L4 L4 L4*"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/barrier3"
        tools:ignore="HardcodedText,RtlHardcoded" />

    <TextView
        android:id="@+id/R4"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:text="R4 R4 R4 R4 R4 R4 R4 R4 R4 R4 R4 R4 R4 R4 R4 R4 R4 R4 R4 R4 R4*"
        app:layout_constraintLeft_toRightOf="@+id/barrierVertical"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/barrier3"
        tools:ignore="HardcodedText" />

</android.support.constraint.ConstraintLayout>
Dayton answered 1/6, 2017 at 14:35 Comment(1)
But how would this work with wrap_content on the ConstraintLayout?Beaudette
D
4

Update : See the accepted answer.

I doubt that there is a way to replicate a GridLayout with a ConstraintLayout in the way that you want purely with XML. If you are willing to let a little code assist the layout, then you can set up the ConstraintLayout to work as a GridLayout using a movable vertical guideline.

Build the XML layout in two columns as you depict. The top of each TextView in the left column will be constrained to the top of the corresponding TextView in the right column, so the left entries will float up or down as the entries on the right increase or decrease in height.

All right column views will be constrained on the left to the vertical guideline mentioned above. The placement of this guideline in the XML should be something reasonable to work with but the actual placement will be made in code and will be adjusted depending upon the width of the widest view on the left.

This is a solution to the problem that you pose but it is not a general solution. The following depends on the height of each TextView on the left being less than or equal to the height of the corresponding TextView on the right.

Here is what the layout looks like in the Android Studio layout editor. I pushed the guideline over to the right to demonstrate how it floats. (Code follows images.)

enter image description here

Here is a screen shot. I hope that you find this useful.

enter image description here

Here is the layout using ConstraintLayout. (Updated since initial post to get wrapping right in left column.)

constrained.xml

<?xml version="1.0" encoding="utf-8"?>
<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:id="@+id/constrained"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.constraint.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_begin="257dp" />

    <TextView
        android:id="@+id/L1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="0dp"
        android:text="A1 A1 A1 A1 A1*"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@id/guideline"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintWidth_default="wrap"
        tools:ignore="HardcodedText" />

    <TextView
        android:id="@+id/L2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="0dp"
        android:layout_marginTop="0dp"
        android:text="B1 B1 B1 B1 B1*"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@id/guideline"
        app:layout_constraintTop_toTopOf="@+id/R2"
        app:layout_constraintWidth_default="wrap"
        tools:ignore="HardcodedText" />

    <TextView
        android:id="@+id/L3"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="0dp"
        android:layout_marginTop="0dp"
        android:text="A2 A2 A2 A2*"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@id/guideline"
        app:layout_constraintTop_toTopOf="@+id/R3"
        app:layout_constraintWidth_default="wrap"
        tools:ignore="HardcodedText" />

    <TextView
        android:id="@+id/L4"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="0dp"
        android:layout_marginTop="0dp"
        android:text="B2 B2 B2 B2 B2*"
        app:layout_constraintHorizontal_bias="0.02"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@id/guideline"
        app:layout_constraintTop_toTopOf="@+id/R4"
        app:layout_constraintWidth_default="wrap"
        tools:ignore="HardcodedText" />

    <TextView
        android:id="@+id/R1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="C1 C1 C1 C1 C1 C1 C1 C1 C1 C1 C1 C1 C1 C1 C1 C1 C1 C1 C1 C1 C1 C1*"
        app:layout_constraintLeft_toRightOf="@id/guideline"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="HardcodedText" />

    <TextView
        android:id="@+id/R2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="D1 D1 D1 D1 D1 D1 D1 D1 D1 D1 D1 D1 D1 D1 D1 D1 D1 D1 D1 D1 D1 D1*"
        app:layout_constraintLeft_toRightOf="@+id/guideline"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/R1"
        tools:ignore="HardcodedText" />

    <TextView
        android:id="@+id/R3"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="C2 C2 C2 C2 C2 C2 C2 C2 C2 C2 C2 C2 C2 C2 C2 C2 C2 C2 C2 C2 C2 C2*"
        app:layout_constraintLeft_toRightOf="@id/guideline"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/R2"
        tools:ignore="HardcodedText" />

    <TextView
        android:id="@+id/R4"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="D2 D2 D2 D2 D2 D2 D2 D2 D2 D2 D2 D2 D2 D2 D2 D2 D2 D2 D2 D2 D2 D2*"
        app:layout_constraintLeft_toRightOf="@+id/guideline"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/R3"
        tools:ignore="HardcodedText" />

</android.support.constraint.ConstraintLayout>

Here is the Activity that adjusts the location of the guideline.

MainActivity.java

package com.example.layout2;

import android.os.Bundle;
import android.support.constraint.ConstraintLayout;
import android.support.constraint.Guideline;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private Guideline mGuideline;
    private ConstraintLayout mConstraintLayout;
    private TextView L1;
    private TextView L2;
    private TextView L3;
    private TextView L4;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.constrained);
        mConstraintLayout = (ConstraintLayout) findViewById(R.id.constrained);
        mGuideline = (Guideline) findViewById(R.id.guideline);
        L1 = (TextView) findViewById(R.id.L1);
        L2 = (TextView) findViewById(R.id.L2);
        L3 = (TextView) findViewById(R.id.L3);
        L4 = (TextView) findViewById(R.id.L4);
        // We will adjust the location of the guideline after layout is completed.
        mConstraintLayout.post(new Runnable() {
            @Override
            public void run() {
                moveGuideline();
            }
        });
    }

    public void moveGuideline() {
        ConstraintLayout.LayoutParams params;

        params = (ConstraintLayout.LayoutParams) mGuideline.getLayoutParams();
        // Find the widest TextView in the left column...
        params.guideBegin = Math.max(Math.max(L1.getWidth(), L2.getWidth()),
                Math.max(L3.getWidth(), L4.getWidth()));
        // ... and set the guideline to the right of the widest one.
        mGuideline.setLayoutParams(params);
    }
}
Dayton answered 24/3, 2017 at 20:25 Comment(0)
R
1

Try this out.

<?xml version="1.0" encoding="utf-8"?>
<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:orientation="horizontal">


    <TextView
        android:id="@+id/textView5"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:text="A1 A1 A1 A1 "
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textView8"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginTop="8dp"
        android:text="C1 C1 C1 C1 "
        app:layout_constraintHorizontal_bias="0.506"
        app:layout_constraintLeft_toRightOf="@+id/textView5"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textView9"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginTop="8dp"
        android:text="B1 B1 B1 B1 "
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/textView8"
        app:layout_constraintTop_toBottomOf="@+id/textView8" />

    <TextView
        android:id="@+id/textView10"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginTop="8dp"
        android:text="D1 D1 D1 D1 "
        app:layout_constraintHorizontal_bias="0.506"
        app:layout_constraintLeft_toRightOf="@+id/textView5"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView5" />

</android.support.constraint.ConstraintLayout>

Something like this. enter image description here

Rabb answered 28/3, 2017 at 12:45 Comment(0)
I
0

Update

The accepted answer is correct. When I answered this on Mar 2017, this was the only option. Two months later, Google introduced barriers.


I don't think it is possible to perform purely with one ConstraintLayout container at this time. I hope that in the future Google will add something like group_id or some other way to calculate layout with "align to group".

In the mean time, I suggest you use ConstraintLayout containers inside ConstraintLayout. This is how I implemented this:

enter image description here enter image description here

To make the rows "grid" like, I used "ConstraintLayout Chains". Number of rows in each ConstraintLayout chain must be identical, so the auto distribution will align rows correctly. (Though they can stay empty or hidden if not used).

The xml gist

Ingroup answered 29/3, 2017 at 9:55 Comment(2)
Do not use constraint layout inside another constraint layout. It breaks the whole concept of a constraint layout.Roughspoken
@Roughspoken Today you're right. on Mar 17, this was the only option. The accepted answer is correct, but it came 3 months after mine, and 1 month after Google introduced barriers.Ingroup

© 2022 - 2024 — McMap. All rights reserved.