49

I've been experimenting with ConstraintLayout, is there a way to set the max width of a view to a percentage of the parent (and a height of match constraint and dimension ratio of 1:1)?

Here is the code without using max width:

<?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"
  android:layout_width="match_parent"
  android:layout_height="wrap_content">

   <FrameLayout
      android:id="@+id/frameLayout3"
      android:layout_width="0dp"
      android:layout_height="259dp"
      android:layout_marginEnd="8dp"
      android:layout_marginStart="8dp"
      android:background="@android:color/black"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintHorizontal_bias="0.0"
      app:layout_constraintStart_toEndOf="@+id/imageView"
      app:layout_constraintTop_toTopOf="parent"/>

   <ImageView
      android:id="@+id/imageView"
      android:layout_width="0dp"
      android:layout_height="0dp"
      android:src="@android:drawable/ic_menu_add"
      app:layout_constraintBottom_toBottomOf="@+id/frameLayout3"
      app:layout_constraintDimensionRatio="1:1"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      app:layout_constraintWidth_percent="0.3"/>

</android.support.constraint.ConstraintLayout>

This is the result:

Tablet:

Tablet

Phone:

Phone

Zoe
  • 27,060
  • 21
  • 118
  • 148
Hyhage
  • 493
  • 1
  • 4
  • 4
  • Try [this](https://developer.android.com/reference/android/support/percent/PercentRelativeLayout.html) link. – Cagri Yalcin Mar 13 '18 at 11:50
  • 1
    I'd prefer to avoid using PercentLayout though, as it was deprecated in favour of ConstraintLayout. – Hyhage Mar 13 '18 at 12:19
  • Then you can use `LinearLayout` – Cagri Yalcin Mar 13 '18 at 14:01
  • Sorry if I'm not being clear, I don't actually want to use percentages for width/height, I just want the view to be limited by a percentage. Using a percentage (using weight or with a guideline) makes it look like this on phone: https://i.imgur.com/4fjmryf.png which is fine, but not on tablet: https://i.imgur.com/27F39Lq.png . The ImageView should not be larger than the FrameLayout on the right. – Hyhage Mar 13 '18 at 14:45

4 Answers4

159

I achieved the max width percentage using two attributes:

app:layout_constraintWidth_max="wrap"
app:layout_constraintWidth_percent="0.4" 

Example:

<TextView
    android:id="@+id/textView2"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginStart="8dp"
    android:layout_marginTop="8dp"
    android:text="Helo world"
    android:textAlignment="viewStart"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintWidth_max="wrap"
    app:layout_constraintWidth_percent="0.4" />

The text width will increase to 40% of parent and then wrap if the content exceeds that.

vman
  • 1,264
  • 11
  • 20
Thomson Varghese
  • 1,636
  • 1
  • 11
  • 5
  • @thomson-varghese, I've tried your answer, but I've not received correct result/ Maybe, I've done something wrong. Can you check my code, please and write, how to set max width as percentage of parent, but just "regular" width should be like wrap_content: 1) [first variant](https://imgur.com/mZbQDdO) , when text is to long, and width should be as % from parent 2) [second variant](https://imgur.com/Txzoqkg), when width shoundn't be so huge, jast wrap_content [My code](https://gist.github.com/GrishinSergey/e1d7b670a73f261f463559ae088c7c55) – Sergey Grishin Jul 28 '19 at 15:42
  • 1
    nice job! could you please explain what does `layout_constraintWidth_max="wrap"` actually mean? – momvart Apr 30 '20 at 11:42
  • does not work when such textview is placed in vertical Flow when it obscures view being above and below – Michał Ziobro Jun 15 '20 at 08:34
  • but if taken space is less than 0.4 pct, it still will be minimum matched 0.4 pct – user924 Jul 21 '20 at 14:42
  • it doesn't wrap if the text is in multiple lines – David Ibrahim Oct 19 '20 at 07:08
  • Works great, except when `android:text=""` the view takes up the maximum % of the width, even though I want it to be 0dp (or at least close to it). My workaround is setting a space instead of an empty string when that would be set. – z3ntu Apr 09 '21 at 14:54
  • Very useful, worked for me! Remember to set 0dp on both views in the horizontal chain and set the chain style to spread_inside – reavcn Oct 08 '21 at 16:11
  • Thanks @reavcn, it should be horizontal chain for both view. if not, it always set the width 0.4 – RoShan Shan Dec 13 '21 at 02:34
  • In my case, adding `app:layout_constrainedWidth="true"` was needed along with changing layout_width to `"wrap_content"`. It was a chain of a TextView + a RecyclerView, and the latter needed to have its width wrap the content but not to exceed 70% of the parent width. – jk7 Aug 28 '23 at 20:36
1

If I'm understanding your problem right then You're almost there. I think you're giving frameLayout static height that's why it is not giving the appropriate result on tablet.. because you set the height according to phone preview. What you need to do is make the height of frameLayout relative to imageView.. so when imageView grows in size the frameLayout also grows with it.

I think you should do something like this

<?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"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:src="@android:drawable/ic_menu_add"
        app:layout_constraintDimensionRatio="1:1"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintWidth_percent="0.3" />    

    <FrameLayout
        android:id="@+id/frameLayoutTwo"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="@android:color/black"
        app:layout_constraintBottom_toBottomOf="@+id/imageView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/imageView"
        app:layout_constraintTop_toTopOf="parent" /> 

</android.support.constraint.ConstraintLayout>

I checked and this gives the same result in phone and tablet. Correct me if I misunderstood your problem.

Somesh Kumar
  • 8,088
  • 4
  • 33
  • 49
0

No, there is not. I think we should file a feature request. I am pretty sure that PercentRelativeLayout also does not have this feature. (This answer is accurate for ConstraintLayout 1.1)

androidguy
  • 3,005
  • 2
  • 27
  • 38
0

You can set a LinearLayout or other ViewGroup as exact percent width and place your wrap_content view inside this layout.

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

            <ImageView
                android:id="@+id/image_place_holder"
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:scaleType="centerCrop"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <!-- This LinearLayout will have exact 80% of parent width-->
            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:orientation="horizontal"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintWidth_percent="0.8">

                
                <!-- This MaterialCardView will get a wrap_content width, 
                with a max width of his parent LinearLayout, which is 80% -->
                <com.google.android.material.card.MaterialCardView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="@dimen/half_activity_horizontal_margin"
                    android:layout_marginTop="@dimen/half_activity_vertical_margin"
                    android:layout_marginEnd="@dimen/half_activity_horizontal_margin"
                    android:layout_marginBottom="@dimen/half_activity_vertical_margin"
                    android:backgroundTint="?attr/colorPrimary">

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:maxLines="1"
                        android:paddingStart="@dimen/half_activity_horizontal_margin"
                        android:paddingEnd="@dimen/half_activity_horizontal_margin"
                        android:text="Lorem ipsum"
                        android:textAppearance="?attr/textAppearanceCaption" />

                </com.google.android.material.card.MaterialCardView>

            </LinearLayout>

        </androidx.constraintlayout.widget.ConstraintLayout>