'How to use wrap_content with a maximum width?

I am trying to layout a view that should wrap its content, but it shouldn't be more than ~100dp less than its parent width. How can I do that using a RelativeLayout or some other layout? What I have right now will always make the view 100dp less than its parent so that there is space for another view.

This picture is an example of what I have:

enter image description here

As you can see, the text doesn't fill the whole box, so it could be smaller. But, it should never be larger than 100dp less than its parent, so that there is room for the time the message was sent.

This is my layout.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:clipChildren="false"
    android:orientation="vertical"
    android:paddingBottom="10dp"
    android:paddingRight="@dimen/horizontalMargin"
    android:paddingTop="10dp">


    <TextView
        android:id="@+id/message_holder"
        android:layout_toLeftOf="@id/blank"
        android:layout_alignParentLeft="true"
        android:layout_marginLeft="@dimen/horizontalMargin"
        android:background="@drawable/message_corners"
        style="@style/white_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="alsdkjf; alsdkf" />

    <RelativeLayout
        android:id="@+id/blank"
        android:layout_width="wrap_content"
        android:layout_height="1dp"
        android:layout_alignParentRight="true"
        android:minWidth="100dp">

    </RelativeLayout>

    <TextView
        android:minWidth="100dp"
        android:id="@+id/time"
        style="@style/gray_text"
        android:layout_toRightOf="@id/message_holder"
        android:paddingLeft="10dp"
        android:text="Yesterday,\n11:30 PM" />


    <RelativeLayout
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_alignBottom="@id/message_holder"
        android:layout_alignParentLeft="true"
        android:background="@drawable/triangle" />

</RelativeLayout>

I have tried using the "minWidth" property on a blank view to the right of the message box to provide spacing, but it doesn't resize to be larger (which would make the message box smaller). When I don't have the blank view, and simply place the time TextView to the right of the message box, then that TextView isn't visible when the message box expands.

Update:

This is my "message_corners.xml":

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">


    <solid
        android:color="@color/green" >
    </solid>


    <padding
        android:left="10dp"
        android:top="10dp"
        android:right="10dp"
        android:bottom="10dp"    >
    </padding>


    <corners
        android:radius="10dp">
    </corners>

</shape>

Update 2:

This is what I am looking for in a layout with short text:

enter image description here

And this is what I am looking for in a layout with long text:

enter image description here



Solution 1:[1]

Here you go, a layout that does exactly what you want.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:clipChildren="false"
    android:paddingBottom="10dp"
    android:paddingRight="10dp"
    android:paddingTop="10dp">


    <RelativeLayout
        android:id="@+id/blank"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#aaaaaa">
        <LinearLayout
            android:id="@+id/message_container"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingRight="100dp">
            <TextView
                android:id="@+id/message"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Hello?"
                android:background="#00ff00" />
        </LinearLayout>

        <TextView
            android:id="@+id/time"
            android:layout_width="100dp"
            android:layout_height="wrap_content"
            android:layout_toRightOf="@id/message_container"
            android:layout_marginLeft="-100dp"
            android:text="12:30 PM"
            android:background="#ff0000" />
    </RelativeLayout>

</RelativeLayout>

Short message Short message

Long message Long message

Solution 2:[2]

I know this is a really old question, but it's a frustrating problem I've encountered several times now and the existing answers weren't quite what I was looking for. Some colleagues and I came up with the following:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:background="#FFFFFF">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#888888"
        android:padding="10dp"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="#00FF00"
            tools:text="Short message."/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:layout_weight="0"
            android:background="#CCCCCC"
            tools:text="Yesterday,\n11:30pm"/>

    </LinearLayout>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:background="#888888"
        android:padding="10dp"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/message_text_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="#00FF00"
            tools:text="Super ultra mega awesome long message which is going to help us take over the world."/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:layout_weight="0"
            android:background="#CCCCCC"
            tools:text="Yesterday,\n11:31pm"/>

    </LinearLayout>

</LinearLayout>

Which looks like this when rendered:

enter image description here

The magic seems to be the zero value for the weight of the text box on the right (in addition to the non-zero weight value of the text box on the left, which some of the other answers already have).

Honestly, I can't explain exactly why it works, but after having looked for a solution to this for so long I'm not questioning it! :)

As an aside, I like this approach because it doesn't require any explicit or minimum widths, any intermediate wrapper views, or the use of clipping settings, margins, padding, etc. to implement view overlay.

Solution 3:[3]

What the author of this question really asks is, how to let the TextView expand to fit the message inside of it without overflowing the time TextView, and without leaving blank spaces.

Since you don't actually know the width of the whole screen, you can't tell your TextView to be 100dp less than it. What you should do is wrap your TextView in a container which will have the toLeftOf rule, with the TextView only wrapping it's contents. This way, the container will expand all the way up to the right (without overflowing the time TextView) but the TextView will only wrap it's text contents (so, it won't extend any blank spaces)

Code

Instead of

<TextView
        android:id="@+id/message_holder"
        android:layout_toLeftOf="@id/blank"
        android:layout_alignParentLeft="true"
        android:layout_marginLeft="@dimen/horizontalMargin"
        android:background="@drawable/message_corners"
        style="@style/white_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="alsdkjf; alsdkf" />

Use

<LinearLayout
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"       
 android:layout_toLeftOf="@id/blank"
 android:layout_alignParentLeft="true"
 android:layout_marginLeft="@dimen/horizontalMargin">

     <TextView
          android:id="@+id/message_holder"
          android:background="@drawable/message_corners"
          style="@style/white_text"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="alsdkjf; alsdkf" />
</LinearLayout>

By the way, your layout isn't very good. You should optimize it.

Solution 4:[4]

You can try the following arrangement of views and their widths:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:tools="http://schemas.android.com/tools"
              android:orientation="horizontal"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:padding="6dp"
        >
    <FrameLayout
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content">
        <TextView 
                android:layout_width="wrap_content" 
                android:layout_height="wrap_content"
                android:background="@android:color/holo_blue_bright"
                tools:text="Some long test is this which is support to wrap at the end of parent view"
                />
    </FrameLayout>
    <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="6dp"
            tools:text="Yesterday,\n 11:30 PM"
            />
</LinearLayout>

Solution 5:[5]

Sat Sri Akal

This can also be achieved using ConstraintLayout

with 2 children in horizontal chain

1st child layout width 0 constraint weight 1 constraint max width wrap

2nd child layout width wrap content

Solution 6:[6]

If you want to make time text on right and text message on its left, you can do something like that ( using this in relative layout) also you can use maxWidth not minWidth

<TextView
        android:id="@+id/view_textView_timeText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"/>

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_toLeftOf="@id/view_textView_timeText"
        android:maxWidth="100dp"/>

Solution 7:[7]

What you could do is put an empty view between the 2 views and keep its width as MATCH_PARENT and assign the textview to leftof this empty view and the empty view to left of the date view. Just make sure to keep the view empty.

Solution 8:[8]

As i understand you want to make the layout or the textview to be 100 dp less than the screen size Which you can do by getting the screen width in pixels which is done by this

Display display  = getWindowManager().getDefaultDisplay();
 Point size = new Point();
 display.getSize(size);
 int width = size.x;
 int height = size.y;

Then you could set the textbiew width to be less 100dp from the screen size hope this help P.s I think you might want to convert dp to px but i am not sure

Solution 9:[9]

You can do like this(not the direct answer for the question ):

 <LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:gravity="bottom"
    android:orientation="horizontal">


    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:ellipsize="end"
            android:maxLines="1"
            android:paddingLeft="45px"
            android:text="asdfadsfsafasdfsakljkljkhjhkkhjkjhjkjhjkhjkhljkhlkhjlkjhljkhljkhlkjhljkhljkhlfasd"
            android:textColor="#4a4a4a"
            android:textSize="40px" />
    </LinearLayout>


    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:maxLines="1"
        android:paddingLeft="45px"
        android:paddingRight="48px"
        android:text="2017.08.09 13:00"
        android:textColor="#9b9b9b"
        android:textSize="34px" />
</LinearLayout>

Solution 10:[10]

I have a common solution to solve this kind of layout question:

Create a specific ViewGroup!

For the question above, the key point is how to set the correct maxWidth to the content view.

  1. Create a SpecialViewGroup. The contentView is the left view, and the timeView is the right view.
class SpecialViewGroup @JvmOverloads constructor(
  context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {

  private lateinit var contentView: TextView
  private lateinit var timeView: TextView

  override fun onAttachedToWindow() {
    super.onAttachedToWindow()
    contentView = findViewById(R.id.content)
    timeView = findViewById(R.id.time)
  }

  override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    // measure the timeView firstly, because the contentView's maxWidth rely on it.
    timeView.measure(
      MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
      MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
    )

    // then caculate the remained space for the contentView
    val parentWidth = MeasureSpec.getSize(widthMeasureSpec)
    val paddingHorizontal = paddingStart + paddingEnd
    val view1MaxWidth = parentWidth - timeView.measuredWidth - paddingHorizontal

    // set the maxWidth to the contentView
    contentView.maxWidth = view1MaxWidth

    // The rest thing will be handed over by LinearLayout
    super.onMeasure(widthMeasureSpec, heightMeasureSpec)
  }
}
  1. Use the SpecialViewGroup in your layout, like the usual LinearLayout.
  <com.example.SpecialViewGroup
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#FFBB86FC"
    android:orientation="horizontal"
    android:padding="10dp">

    <TextView
      android:id="@+id/content"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:background="#FF3700B3"
      android:padding="10dp"
      android:text="adaasdasdasasdadasdasdaaaaaaaaaaaaaaaa"
      android:textColor="@color/white" />

    <TextView
      android:id="@+id/time"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_gravity="center"
      android:background="#FF018786"
      android:padding="10dp"
      android:text="1970-01-01"
      android:textColor="@color/white" />

  </com.example.archview.SpecialViewGroup>

And the result:

enter image description here enter image description here

The benefits of this approach are obvious:

  1. No extra nesting Layout.
  2. Common to solve the similar layout questions.

Solution 11:[11]

Had the similar issue. Made it works with constraint.

<ConstraintLayout>

    <TextView
         android:id="@+id/title"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:ellipsize="end"
         android:maxLines="2"
         app:layout_constraintEnd_toStartOf="@+id/option_info"
         app:layout_constraintHorizontal_chainStyle="packed"
         app:layout_constraintHorizontal_weight="1"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintWidth_max="wrap" />

    <ImageView
         android:id="@+id/option_info"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:src="@drawable/ic_info"
         app:layout_constraintBottom_toBottomOf="@+id/title"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toEndOf="@+id/title"
         app:layout_constraintTop_toTopOf="@+id/title" />

</ConstraintLayout>

Solution 12:[12]

A solution with ConstraintLayout using

  • app:layout_constrainedWidth
  • layout_constraintHorizontal_bias
  • layout_constraintHorizontal_chainStyle

.

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

    <EditText
        android:id="@+id/edt_left"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Sample content"
        app:layout_constrainedWidth="true"
        app:layout_constraintEnd_toStartOf="@id/button_right"
        app:layout_constraintHorizontal_bias="0"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button_right"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Right Button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/edt_left"
        app:layout_constraintTop_toTopOf="parent" />


</androidx.constraintlayout.widget.ConstraintLayout>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 clownba0t
Solution 3
Solution 4 S.D.
Solution 5 Jasdeep Singh
Solution 6 M.Baraka
Solution 7 Bhargav
Solution 8
Solution 9 Ado
Solution 10 Crow
Solution 11
Solution 12 Linh