This question already has an answer here:
I'm laying out 3 views horizontally on the screen (fixed-size image and 2 single-line text views: leftTextView
and rightTextView
) and I'm trying to get the rightTextView
to hug against the leftTextView
, but in the event that the width of both labels would exceed the screen size, truncate the leftTextiew
.
Example of desired functionality:
|img|leftText|rightText| ||(end of screen)
|img|leftTextMedium|rightText| ||
|img|leftTextTooLongSoTrunc...|rightText||
What's actually happening:
|img|leftText|rightText| ||(end of screen)
|img|leftTextPrettyLongButNotHuge|rightT||
|img|leftTextWhichIsIncrediblyLongBlahBl||
Currently, if the size of both text views exceeds the view's width, the rightTextView
ends up getting squished down to make room, even though I've set android:ellipsize="end"
on the leftTextView
.
Is there a way to make the leftTextView
have a "truncation priority" that says it should truncate if it would cause other views to not fit? Or on the other side, can I set the rightTextView
to have a "compression resistance" in order to keep it from being squished to make room for the leftTextView
? Here's a sample of my XML:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/fixedWidthImage"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<TextView
android:id="@+id/leftTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@+id/fixedWidthImage"
android:layout_toRightOf="@id/fixedWidthImage"
android:maxLines="1"
android:textSize="18sp"
android:ellipsize="end" />
<TextView
android:id="@+id/rightTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@+id/leftTextView"
android:layout_toRightOf="@+id/leftTextView"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:maxLines="1"
android:textSize="12sp" />
</RelativeLayout>
Things I've tried:
- In the
leftTextView
, add atoStartOf
/toLeftOf
therightTextView
(crashes the app) Instead of aligning the
rightTextView
to the end/right of theleftTextView
, flip it and align theleftTextView
to the start/left of therightTextView
. This results in therightTextView
being anchored to the end of the screen instead of directly to the right of theleftTextView
. Example of how this ends up looking:|img|leftText |rightText|| |img|leftTextMedium |rightText|| |img|leftTextTooLongSoTrunc...|rightText||
Even if I set
android:minWidth="25dp"
just to ensure at least part of therightTextView
is visible (which I don't want to do because this text length is variable) it still ends up compressing the width to make room for theleftTextView
.- Per Emanuel Moecklin's answer, I tried wrapping in a
LinearLayout
and setting the weight on theleftTextView
. The result was visually identical to what I tried in #2.
You can archive this layout by TableLayout
and shrinkColumns
attribute.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Row 1 -->
<TableLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:shrinkColumns="1">
<TableRow
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
android:maxLines="1"
android:ellipsize="end"
android:text="leftText"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
android:maxLines="1"
android:ellipsize="none"
android:text="rightText"/>
</TableRow>
</TableLayout>
<!-- Row 2 -->
<TableLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:shrinkColumns="1">
<TableRow
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
android:maxLines="1"
android:ellipsize="end"
android:text="leftTextPrettyLongButNotHuge"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
android:maxLines="1"
android:ellipsize="none"
android:text="rightText"/>
</TableRow>
</TableLayout>
<!-- Row 3 -->
<TableLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:shrinkColumns="1">
<TableRow
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
android:maxLines="1"
android:ellipsize="end"
android:text="leftTextWhichIsIncrediblyLongBlahBlahBlahBlah"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
android:maxLines="1"
android:ellipsize="none"
android:text="rightText"/>
</TableRow>
</TableLayout>
</LinearLayout>
Here's what I do:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/fixedWidthImage"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/ic_launcher_pro" />
<TextView
android:id="@+id/leftTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical|left"
android:ellipsize="marquee"
android:lines="1"
android:text="This is a very long text, and now even longer" />
<TextView
android:id="@+id/rightTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|left"
android:lines="1"
android:text="This is also quite long" />
</LinearLayout>
The android:layout_weight="1" does the "trick" here. The documentation isn't quite clear on this one though:
For example, if there are three text fields and two of them declare a weight of 1, while the other is given no weight, the third text field without weight will not grow and will only occupy the area required by its content.
(https://developer.android.com/guide/topics/ui/layout/linear.html#Weight)
It doesn't elaborate on the case in which there isn't space to fill but not enough space for all views. One interpretation of the mentioned piece of documentation would be that a field without weight (the rightTextView in this case) will occupy the area required by its content while a field with weight (the leftTextView in this case) will "grow" (as in negative growth) and that's exactly what happens.
I think you're going to need to make a custom layout. This works well for the sample I made and it meets all of the requirements you posted above, but you'll need to add a touch more math the code to account for layout_margins and padding.
The basic idea of what's going on here is I subclassed LinearLayout and in the onMeasure pass, I check to make sure that right view has the option to take up as much space as possible. Note that I have to remeasure the right view and not just get the measured width because linearlayout isn't going to give it enough space. Once you have how much room the right view takes up, check to see if the middle view (called leftView...sorry) is taking up more space than you want. If it does, just give it the space that's left over.
Based upon your requirements of it being in a RecyclerView... I know you said you wanted a pure xml solution, but you're going to get better performance if you tackle it in code as xml 1. may be impossible and 2. will require more computations than just doing it yourself directly.
public class CustomLinearLayout extends LinearLayout {
private View child0;
private TextView leftView, rightView;
public CustomLinearLayout(Context context) {
super(context);
}
public CustomLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (rightView == null) {
child0 = getChildAt(0);
leftView = (TextView)getChildAt(1);
rightView = (TextView) getChildAt(2);
}
int spec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.AT_MOST);
rightView.measure(spec, spec);
int remaining = getMeasuredWidth() - child0.getMeasuredWidth() - rightView.getMeasuredWidth();
if (leftView.getMeasuredWidth() > remaining) {
int specW = MeasureSpec.makeMeasureSpec(remaining, MeasureSpec.AT_MOST);
int specH = MeasureSpec.makeMeasureSpec(leftView.getMeasuredHeight(), MeasureSpec.EXACTLY);
leftView.measure(specW, specH);
}
}
XML layout
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.joshua.myapplication.MainActivity">
<com.example.joshua.myapplication.CustomLinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:background="#FF0000"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textSize="18sp"
android:text="My Left View With Really Really Long Text That should fill the screen"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1"
android:textSize="18sp"
android:text="My Right View"
/>
</com.example.joshua.myapplication.CustomLinearLayout>
<com.example.joshua.myapplication.CustomLinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="20dp">
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:background="#FF0000"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textSize="18sp"
android:text="My Left View with short text"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1"
android:textSize="18sp"
android:text="My Right View"
/>
</com.example.joshua.myapplication.CustomLinearLayout>
</LinearLayout>
Just use android:maxWidth
property to your left textView. So, your leftview will never exceed that limit. and there will be always space for your right view.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/fixedWidthImage"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:src="@drawable/ic_launcher" />
<TextView
android:id="@+id/leftTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/fixedWidthImage"
android:layout_toRightOf="@+id/fixedWidthImage"
android:ellipsize="end"
android:lines="1"
android:maxWidth="250dp"
android:text="This is a Long left Text which never ends and You'll be tired after reading this long stuff.. blah blah... " />
<TextView
android:id="@+id/rightTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/leftTextView"
android:layout_toRightOf="@+id/leftTextView"
android:ellipsize="end"
android:lines="1"
android:text="Right Text" />
</RelativeLayout>
However, limitation with solution is you have to give fixed value in maxWidth property. So, it'll display different look on different screen size. Though, in most cases you'll not face issue.
Also, for displaying long textView in single line android:ellipsize="marquee"
is good option. As it'll scroll your view horizontal. But don't forget to write this textView.setSelected(true)
in your code to make it work. Detail
来源:https://stackoverflow.com/questions/38860417/android-textview-truncation-priority-or-compression-resistance