hallz12
hallz12

Reputation: 679

How to support wrap_content and match_parent condition with 3 views in horizontal position in Android Constraint Layout

I want to create a compound view that contains 3 views in horizontal position. When I set the width of this compound view to WRAP_CONTENT, I want all those 3 views locate side by side as it is WRAP_CONTENT. But when I set the width to MATCH_PARENT, I want the second view to take over the whole space of the screen (but still second view located beside the first view).

Example:

WRAP_CONTENT:

|view1 view2 view3 --empty-space--|

MATCH_PARENT:

|view1 view2 --empty-space-- view3|

Due to some reason, I have to use constraint layout here.

I have tried the code below. When I tried to change the width of second view to WRAP_CONTENT, and set the whole constraint layout to MATCH_PARENT, the second view become located in the center (while what I want is to keep the location on the left)

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <TextView
        android:id="@+id/view1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:text="view1"/>

    <TextView
        android:id="@+id/view2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toEndOf="@id/test1"
        app:layout_constraintEnd_toStartOf="@id/test3"
        android:text="view2"/>

    <TextView
        android:id="@+id/view3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:text="view3"/>
</androidx.constraintlayout.widget.ConstraintLayout>

Thank you. I appreciate any solutions.

Upvotes: 2

Views: 2399

Answers (4)

avisper
avisper

Reputation: 908

add this to the A view

app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintHorizontal_chainStyle="packed"

and set B to be a dependent of A:

ex:

 <TextView
    android:id="@+id/tv_description"
    android:layout_width="0dp"
    ...
    app:layout_constraintHorizontal_bias="0.0"
    app:layout_constraintHorizontal_chainStyle="packed"
    app:layout_constraintEnd_toStartOf="@+id/btn_allow_permission"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/tv_permission_title" />

<Button
    app:layout_constraintStart_toEndOf="@+id/tv_description"
    ...
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

Upvotes: 0

Martin Marconcini
Martin Marconcini

Reputation: 27256

I'm not 100% sure I understand what you're trying to accomplish, but I'll extend (already given answers) for each case I can imagine:

| V1 | V2 | V3 ...... | you simply need to tell the 3rd view to match_constraints with android:layout_width="0dp" and it will occupy all the space left by the other views (which have wrap).

This would produce the same output I understand from here:

|view1 view2 view3 --empty-space--|

Unless by -- empty space -- you meant |V1|V2|V3| ..... | in which case you'd need a Constraint Layout Chain and bias:

On the left-most view, add:

app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintHorizontal_chainStyle="packed"

and ensure all views have a connection between them, like view1 (left most) would have: startToStart=parent, endToStart=view2. View2 would then have startToEnd=view1, endToStart=view3, and view3... startToEnd=view2, endToEnd=parent

You get the idea.

This would pack all views to the start leaving empty space at the end. The above would work regardless of the parent's constrains (match/wrap) since the engine will position/size views based upon the available space anyway.

Now, for your MATCH_PARENT example:

|view1 view2 --empty-space-- view3|

It's unclear (to me) what you mean by --empty space--, does this mean you want the view2 to wrap its contents? or do you want view2 to use all the available space and push view3 to the end?

I'll assume you mean all views are wrapping and there's empty space. There's a good answer about how to achieve that (hint, you'd need to chain in a space in between) if you don't know the exact dimensions, because the engine would have no way to determine how much space each view will consume before calculating the sizes and wrapping them.

Now, if you don't mind view2 taking all the remaining space and breaking the chain (only when needed), you can do the normal biased chain, but use a default spread condition for wrapping:

And so it would look like this:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <TextView
            android:id="@+id/view1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintHorizontal_chainStyle="packed"
            app:layout_constraintHorizontal_bias="0"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toStartOf="@id/view2"
            app:layout_constraintTop_toTopOf="parent"
            android:text="view1"/>

    <TextView
            android:id="@+id/view2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toEndOf="@id/view1"
            app:layout_constraintEnd_toStartOf="@id/view3"
            app:layout_constraintWidth_default="spread"
            android:text="view2"/>

    <TextView
            android:id="@+id/view3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toEndOf="@id/view2"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="1.0"
            android:text="view3"/>

</androidx.constraintlayout.widget.ConstraintLayout>

And this would produce something like:

| V1 | V2.............|V3|

Which may or may not work for you.

Upvotes: 3

Pawel Laskowski
Pawel Laskowski

Reputation: 6346

Put your Views in a packed horizontal chain with a bias of 0 to align them to the left.

Packed chain will group the Views side by side in the middle of the parent ConstraintLayout. When none of your Views has width set to 0dp (match constraint) then you can control the horizontal positioning of this group by setting the bias. If you set your second View to 0dp then it will take all remaining space.

You can read more about chains in the documentation.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/view1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintHorizontal_chainStyle="packed"
            app:layout_constraintHorizontal_bias="0"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toStartOf="@id/view2"
            app:layout_constraintTop_toTopOf="parent"
            android:text="view1"/>

        <TextView
            android:id="@+id/view2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toEndOf="@id/view1"
            app:layout_constraintEnd_toStartOf="@id/view3"
            android:text="view2"/>

        <TextView
            android:id="@+id/view3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toEndOf="@id/view2"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            android:text="view3"/>
</androidx.constraintlayout.widget.ConstraintLayout>

Edit:

There are two options I can think of.

First, while setting the width of your parent layout to wrap_content you can also set the width of the second View to wrap_content.

Second option is to add a Space between the second and third View to fill the remaining space when the parent layout is set to match_constraint or shrink when you set it to wrap_content.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <TextView
            android:id="@+id/view1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintHorizontal_chainStyle="packed"
            app:layout_constraintHorizontal_bias="0"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toStartOf="@id/view2"
            app:layout_constraintTop_toTopOf="parent"
            android:text="view1"/>

    <TextView
            android:id="@+id/view2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toEndOf="@id/view1"
            app:layout_constraintEnd_toStartOf="@id/space"
            android:text="view2"/>

    <Space
            android:id="@+id/space"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toEndOf="@id/view2"
            app:layout_constraintEnd_toStartOf="@id/view3"
            app:layout_constraintTop_toTopOf="parent"/>

    <TextView
            android:id="@+id/view3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toEndOf="@id/space"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            android:text="view3"/>

</androidx.constraintlayout.widget.ConstraintLayout>

Upvotes: 0

qwerty qwerty
qwerty qwerty

Reputation: 26

ConstraintLayout provides an ability to determine chains between child views. There are available packed, spread_inside & spread.

You can read about chaining here: https://constraintlayout.com/basics/create_chains.html

Upvotes: 0

Related Questions