Comtaler
Comtaler

Reputation: 1630

TextView with long text pushes other views off screen

I am trying to achieve the following layout:

+----------------------------------------+
| [icon] [text] [icon]                   |
+----------------------------------------+
| [icon] [very loooooooooooooooooo [icon]|
|         oooooooooooooooong text]       |
+----------------------------------------+

When the text is short, the icon on the right needs to be right next to the text (not right aligned). When the text is long, I need the text to be wrapped.

I have tried to use LinearLayout and RelativeLayout, but the icons are still got pushed out when I have a long text. Here are the layouts I have tried:

LinearLayout:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:orientation="horizontal">

    <TextView
        android:id="@+id/left"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="left"/>

    <TextView
        android:id="@+id/middle"
        android:text="a long long string, a long long string, a long long string, a long long string, a long long string, a long long string, "
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#D0E198"/>

    <TextView
        android:id="@+id/right"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="right"/>
</LinearLayout>

RelativeLayout:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="wrap_content">

    <TextView
        android:id="@+id/left"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="left"/>

    <TextView
        android:id="@+id/middle"
        android:text="a long long string, a long long string, a long long string, a long long string, a long long string, a long long string, "
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#D0E198"
        android:layout_toRightOf="@id/left"/>

    <TextView
        android:id="@+id/right"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="right"
        android:layout_toRightOf="@id/middle"/>
</RelativeLayout>

In both cases, the right icon is pushed out of the screen.

I have also tried LinearLayout with layout_weight="1" on the left and right view and 0 on the middle view. That pushes both icons off screen.

Upvotes: 13

Views: 6153

Answers (8)

ChristianoBolla
ChristianoBolla

Reputation: 648

I know this is a late answer on this thread, but I found the solution and would like to share it with others.

To fix this you should be using ConstraintLayout:

<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">
​
<ImageView
    android:id="@+id/iconStart"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/unread_circuler_dot"
    app:layout_constraintEnd_toStartOf="@id/text"
    app:layout_constraintHorizontal_bias="0"
    app:layout_constraintHorizontal_chainStyle="packed"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />
​
<TextView
    android:id="@+id/text"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:ellipsize="end"
    android:maxLines="2"
    android:text="Very long text"
    android:textColor="@color/white"
    app:layout_constraintEnd_toStartOf="@id/iconEnd"
    app:layout_constraintHorizontal_chainStyle="packed"
    app:layout_constraintStart_toEndOf="@id/iconStart"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintWidth_default="wrap" />
​
<ImageView
    android:id="@+id/iconEnd"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/unread_circuler_dot"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_chainStyle="packed"
    app:layout_constraintStart_toEndOf="@id/text"
    app:layout_constraintTop_toTopOf="parent" />
​
​
</androidx.constraintlayout.widget.ConstraintLayout>

The key to this fix is the line app:layout_constraintWidth_default="wrap" Information about this from Google themselves:

Wrap: Expands the view only as much as needed to fit its contents, but still allows the view to be smaller than that if the constraints require it. So the difference between this and using Wrap Content (above), is that setting the width to Wrap Content forces the width to always exactly match the content width; whereas using Match Constraints with layout_constraintWidth_default set to wrap also allows the view to be smaller than the content width.

More information can be found here

Update!!

Since app:layout_constraintWidth_default="wrap" has been deprecated, the main way to achieve the same effect is by using it with

android:layout_width="wrap_content"
app:layout_constrainedWidth="true"

Upvotes: 8

Nader Baltaji
Nader Baltaji

Reputation: 56

You should set the second view's android:layout_weight to a value less than 1, so that it keeps expanding until the last view reaches the tip of the layout.

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

    <TextView
        android:id="@+id/right"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="right"/>

    <TextView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="0.5"
        android:maxLines="1"
        android:ellipsize="end"/>

    <TextView
        android:id="@+id/left"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="left"/>
</LinearLayout>

Upvotes: 0

Djangow
Djangow

Reputation: 391

After some hair pulling and no obvious sollution I came to this hack. The custom textview class only works for textviews and I needed it to work for a layout with multiple things. Limitation of this is that you have to have the max width defined for the right element. This does remind me a bit of the old html IE 6 days..

<RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <FrameLayout
            android:id="@+id/resizing_element"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingRight="112dp" // Right padding to the amount of space you need for the right element
            android:background="@drawable/bg_chat_in" // Background drawable has the same transparant padding added to complete the illusion of this hack
            android:text="Long text content" />

        <FrameLayout
            android:id="@+id/meta_container"
            android:layout_width="112db" // This one has to have a predefined size.
            android:layout_height="wrap_content"
            android:layout_alignRight="@+id/content_container" // Align right places this element in the right padding of the first element>

        </FrameLayout>

    </RelativeLayout>

Upvotes: 0

Jenus Dong
Jenus Dong

Reputation: 304

You can reference this; First you layout the first left Icon, then use the reference method to layout [text view and right icon];

It works absolutely!

Of course, According to your requirement, the last right icon can set as the middle textview DrawableRight. But this method has one constraint: the the right icon bound limits to textview bound.

Upvotes: -1

Comtaler
Comtaler

Reputation: 1630

Here is what I have done to achieve what I want:

I added layout_weight="1" to the middle TextView

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:orientation="horizontal">

    <TextView
        android:id="@+id/left"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="left"/>

    <WrapWidthTextView
        android:id="@+id/middle"
        android:text="a long long string, a long long string, a long long string, a long long string, a long long string, a long long string, "
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#D0E198"
        android:layout_weight="1"/>

    <TextView
        android:id="@+id/right"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="right"/>
</LinearLayout>

However, when the middle text is short, it will still occupy the entire empty middle space and pushes the right TextView all the way to the right. I have to subclass TextView:

public class WrapWidthTextView
  extends TextView {

    // constructors here

    @Override protected void onMeasure (final int widthMeasureSpec, final int heightMeasureSpec) {
        super.onMeasure (widthMeasureSpec, heightMeasureSpec);

        Layout layout = getLayout ();
        if (layout != null) {
            int width = (int) Math.ceil (getMaxLineWidth (layout))
                        + getCompoundPaddingLeft () + getCompoundPaddingRight ();
            int height = getMeasuredHeight ();
            setMeasuredDimension (width, height);
        }
    }

    private float getMaxLineWidth (Layout layout) {
        float max_width = 0.0f;
        int lines = layout.getLineCount ();
        for (int i = 0; i < lines; i++) {
            if (layout.getLineWidth (i) > max_width) {
                max_width = layout.getLineWidth (i);
            }
        }
        return max_width;
    }
}

With WrapWidthTextView, when the text is short, it will shrink the view to fit the text.

And that solved my problem. I hope this will help others as well.

Upvotes: 6

QuestionAndroid
QuestionAndroid

Reputation: 891

you can check :

(middle :

 android:layout_toLeftOf="@+id/right"
        android:layout_toRightOf="@+id/left"

)

(right :android:layout_alignParentRight="true")

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="wrap_content">

    <TextView
        android:id="@+id/left"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="left"/>

    <TextView
        android:id="@+id/middle"
        android:text=" a long long string, a long long string, a long long string, a long long string, a long long string, a long long string,  "
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#D0E198"
        android:layout_centerInParent="true"
        android:layout_toLeftOf="@+id/right"
        android:layout_toRightOf="@+id/left"/>

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

Upvotes: 0

Gabe Sechan
Gabe Sechan

Reputation: 93561

Set a maxWidth to the text field. It won't get bigger than that. You can also set maxLines to stop wrapping to a new line.

Upvotes: 2

Evan Bashir
Evan Bashir

Reputation: 5551

Use the first layout, but add weighting to your middle textview.

Example:

 <TextView
        android:id="@+id/middle"
        android:text="a long long string, a long long string, a long long string, a long long string, a long long string, a long long string, "
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="wrap_content"
        android:background="#D0E198"/>

Make sure to use 0dp as your width!

Note: This answer is if you want the icon to be right aligned. Which, I'm not sure that's what you need.

Upvotes: 3

Related Questions