Constraint layout min width makes view take all available space when its width is below the minimum

Viewed 101

I have a CardView inside a ConstraintLayout, and I want the CardView to have a width of 70%, a min width of 300dp, and a max of 450dp.

The max works fine, if 70% is more than 450dp, the CardView width is set to 450dp.

The min width however, behaves very strangely.

When 70% is less than 300dp, instead of making the CardView 300dp wide, it makes it as wide as the available space, so 100%.

Here is the CardView inside the layout:

<androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
        <com.google.android.material.card.MaterialCardView
                android:id="@+id/balanceCard"
                android:layout_width="0dp"
                android:layout_height="0dp"
                app:cardCornerRadius="40dp"
                app:cardElevation="15dp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintDimensionRatio="1:1"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintWidth_min="300dp"
                app:layout_constraintWidth_percent="0.7"/>
<androidx.constraintlayout.widget.ConstraintLayout/>

This is the layout with min width of 280dp

And this is the layout with min width of 300dp

I don't know what is happening, and I would appreciate any help.

1 Answers

This looks like a bug to me. The width of the CardView is incorrect when the minimum width exceeds 70% of the layout's width. For a Nexus 6 emulator (dp==3.5px), the ConstraintLayout width would be 1440px and 70% of that is 1008px. (I am using px instead of dp because I think that it makes what's happening clearer.)

When the minimum width is set to 288dp (1008px), everything looks good.

<View
    android:id="@+id/balanceCard"
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:background="@android:color/holo_blue_light"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintDimensionRatio="1:1"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintWidth_min="288dp"
    app:layout_constraintWidth_percent="0.7" />

enter image description here

I am using a straight View instead of a CardView for simplicity.

However, if we set the minimum width to 289dp (1011.5px), we see the problem.

enter image description here

Notice that 288dp (1008px) is equal to 70% of the layout's width (1008px) while 289dp (1011.5px) is greater than 70% of the layout's width.

It is interesting that, if we keep the minimum width at 289dp but remove the end constraint, the width is correct:

<View
    android:id="@+id/balanceCard"
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:background="@android:color/holo_blue_light"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintDimensionRatio="1:1"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintWidth_min="289dp"
    app:layout_constraintWidth_percent="0.7" />

enter image description here

I will note that I am using version 2.1.4 of ConstraintLayout.

The only work-around that I can see One work-around is, upon layout, check the width of the view and if it exceeds 70% of the layout's width, set the width to the minimum width that you want.

Another work-around that is more complex but involves just XML is to split the 70% behavior and the minimum width into two separate Space views. You can then set a barrier to the start and end of these two views and constrain the card view to the barriers. Remove the minimum width and 70% width attributes from the card view. This looks like this:

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <Space
        android:id="@+id/spacePct"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintWidth_percent="0.7" />

    <Space
        android:id="@+id/spaceMinwidth"
        android:layout_width="289dp"
        android:layout_height="0dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <androidx.constraintlayout.widget.Barrier
        android:id="@+id/barrierStart"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:barrierDirection="start"
        app:constraint_referenced_ids="spacePct,spaceMinwidth"/>

    <androidx.constraintlayout.widget.Barrier
        android:id="@+id/barrierEnd"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:barrierDirection="end"
        app:constraint_referenced_ids="spacePct,spaceMinwidth"/>

    <View
        android:id="@+id/balanceCard"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="@android:color/holo_blue_light"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="1:1"
        app:layout_constraintEnd_toEndOf="@id/barrierEnd"
        app:layout_constraintStart_toStartOf="@id/barrierStart"
        app:layout_constraintTop_toTopOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

If you agree with my assessment, you may want to consider submitting a bug report on the Issue Tracker, but that is totally up to you.

Related