I need to achieve the following layout:
I have two TextViews in a relative layout: the green one is fixed text with wrap_content
, the black one has dynamic text with wrap_content
. The black text can change and become very long. I want the black TextView to expand with the text until the green view reaches the end of the parent. If that happens, the black TextView should stop expanding and ellipsize the end.
How can I achieve that?
What I tried:
<RelativeLayout
android:id="@+id/parent"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/leftTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1"
android:layout_alignParentLeft="true"
android:textSize="18sp" />
<TextView
android:id="@+id/rightTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/leftTextView"
android:textSize="18sp" />
</RelativeLayout>
But when the black text gets bigger and bigger it pushes the green one out of the view
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="0">
<TableRow
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
android:maxLines="1"
android:ellipsize="end"
android:text="abcdefghijklmnopqrstuvwxyz"/>
<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="0">
<TableRow
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
android:maxLines="1"
android:ellipsize="end"
android:text="Longer Text view abcdefghijklmnopqrstuvwxyz"/>
<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="0">
<TableRow
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
android:maxLines="1"
android:ellipsize="end"
android:text="Text view that is very logn and will not fit the parent width abcdefghijklmnopqrstuvwxyz"/>
<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 is same question ;)
Here is a layout that will do what you'd like it to:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="left">
<TextView
android:id="@+id/leftTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLefOf="@+id/rightTextView"
android:maxLines="1"
android:textSize="18sp" />
<TextView
android:id="@+id/rightTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:textSize="18sp" />
</RelativeLayout>
The key parts here are the gravity of the relative layout, and aligning the right textview as alignParentRight with the left textview attached to that on the left.
I suggest wrapping two TextView
's into LinearLayout
. Then apply android:weightSum="1"
to this LinearLayout
and apply android:layout_weight="1"
to the child TextView
that must extend.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="1">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="end"
android:maxLines="1"
android:padding="8dp"
android:text="blabla bla bla bla bla bla bla bla bla bla bla bla bla "/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="static text"/>
</LinearLayout>
Remember to set the two atributes this it to work perfectly:
android:ellipsize="end"
android:maxLines="1"
and LinearLayout
must have android:layout_width="wrap_content"
If you would like this ViewGroup
to take whole space, wrap it with another ViewGroup
:
<RelativeLayout
android:layout_height="wrap_content"
android:layout_width="match_parent">
<!--The previous view group-->
</RelativeLayout>
The Layout you are expected can be created with help of layout_weight
attirbute which comes with LinearLayout
. So you may need to use LinearLayout
with weightSum
and layout_weight
(passing to its childview
) attributes.
Here's a layout which divides your width of layout in four parts which divides in to 3:1 between both Textview
. Intacting right TextView
making left TextView
displays text as much as possible it can show without disurbing right TextView
.
Please feel free to try out with variant weightSum
and layout_weight
combination as per your need.
Layout:
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="4" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:singleLine="true"
android:text="StackOverflow StackOverflow StackOverflow "
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:layout_weight="3"
android:maxLines="1"
android:text="TextView"
android:textColor="@android:color/white" />
</LinearLayout>
I set the maxwidth of left textview base of the width of right textview, take a looking my code. hope this will help.
TextView leftText ;
TextView rightText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
leftText = (TextView) findViewById(R.id.leftTextView);
rightText = (TextView) findViewById(R.id.rightTextView);
measureView(leftText);
measureView(rightText);
leftText.setMaxWidth(getWidth() - rightText.getMeasuredWidth());
}
private void measureView(View view) {
ViewGroup.LayoutParams params = view.getLayoutParams();
int childWidth = ViewGroup.getChildMeasureSpec(0, view.getPaddingLeft() + view.getPaddingRight(), params.width);
int childHeight = ViewGroup.getChildMeasureSpec(0, view.getPaddingBottom() + view.getPaddingTop(), params.height);
view.measure(childWidth, childHeight);
}
private int getWidth() {
WindowManager wm = (WindowManager)getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
return display.getWidth();
}
and the xml is:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/leftTextView"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:singleLine="true"
android:text="abadabadabadabadabadabadabadabadabadabaasdfasdfasdfadabadabadabadabadabadabadabadabadabadabaasdfasdfasdfadabadabadabadabadabadabadabadabadabadabaasdfasdfasdfad"/>
<TextView
android:id="@+id/rightTextView"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:textColor="#adc391"
android:singleLine="true"
android:text="adsfasd"/>
</LinearLayout>
this is the screenshot after running:
I have faced this kind of usecase in our app. In our app,there are 3 Views. ImageView | TextView | ImageView
. The two ImageViews must be visible in the layout.The TextView should be ellipsized at the end. And I came up with a custom ViewGroup
.
public class PriorityLayoutHorizontal extends ViewGroup
{
private ArrayList<Integer> mPriorityHighChildren;
public PriorityLayoutHorizontal(Context context)
{
this(context, null);
}
public PriorityLayoutHorizontal(Context context, AttributeSet attrs)
{
this(context, attrs, 0);
}
public PriorityLayoutHorizontal(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
TypedArray typedArray = null;
try
{
typedArray = context.obtainStyledAttributes(attrs, R.styleable.PriorityLayoutHorizontal, defStyleAttr, 0);
final int id = typedArray.getResourceId(R.styleable.PriorityLayoutHorizontal_priorityHigh, -1);
if(id != -1)
{
final int[] highPriorityChildren = getResources().getIntArray(id);
setPriorityHigh(highPriorityChildren);
}
}
finally
{
if(typedArray != null)
{
typedArray.recycle();
}
}
}
public void setPriorityHigh(int... priorityHigh)
{
Integer[] priorityHighInteger = new Integer[priorityHigh.length];
int i = 0;
for(int value : priorityHigh)
{
priorityHighInteger[i++] = Integer.valueOf(value);
}
mPriorityHighChildren = new ArrayList<Integer>(Arrays.asList(priorityHighInteger));
}
int mMaxHeight = 0;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthUsed = 0;
int heightUsed = 0;
final int childCount = getChildCount();
for(int childPosition : mPriorityHighChildren)
{
final View childView = getChildAt(childPosition);
if(childView.getVisibility() != View.GONE)
{
measureChildWithMargins(childView, widthMeasureSpec, widthUsed, heightMeasureSpec, heightUsed);
widthUsed += getMeasuredWidthWithMargins(childView);
heightUsed = Math.max(getMeasuredHeightWithMargins(childView), heightUsed);
}
}
for(int childPosition = 0; childPosition < childCount; childPosition++)
{
if(! mPriorityHighChildren.contains(Integer.valueOf(childPosition)))
{
final View childView = getChildAt(childPosition);
if(childView.getVisibility() != View.GONE)
{
measureChildWithMargins(childView, widthMeasureSpec, widthUsed, heightMeasureSpec, heightUsed);
widthUsed += getMeasuredWidthWithMargins(childView);
heightUsed = Math.max(getMeasuredHeightWithMargins(childView), heightUsed);
}
}
}
mMaxHeight = heightUsed;
int heightSize = heightUsed + getPaddingTop() + getPaddingBottom();
setMeasuredDimension(widthSize, heightSize);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
final int paddingLeft = getPaddingLeft();
final int paddingTop = getPaddingTop();
int spaceUsed = paddingLeft;
for (int childPosition = 0; childPosition < getChildCount(); childPosition++)
{
final View childView = getChildAt(childPosition);
if(childView.getVisibility() != View.GONE)
{
final int top = (mMaxHeight / 2) - (childView.getMeasuredHeight() / 2);
layoutView(childView, spaceUsed, paddingTop + top, childView.getMeasuredWidth(), childView.getMeasuredHeight());
spaceUsed += getWidthWithMargins(childView);
}
}
}
private int getWidthWithMargins(View child)
{
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
return child.getWidth() + lp.leftMargin + lp.rightMargin;
}
private int getHeightWithMargins(View child)
{
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
return child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
}
private int getMeasuredWidthWithMargins(View child)
{
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
return child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
}
private int getMeasuredHeightWithMargins(View child)
{
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
return child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
}
private void layoutView(View view, int left, int top, int width, int height)
{
MarginLayoutParams margins = (MarginLayoutParams) view.getLayoutParams();
final int leftWithMargins = left + margins.leftMargin;
final int topWithMargins = top + margins.topMargin;
view.layout(leftWithMargins, topWithMargins, leftWithMargins + width, topWithMargins + height);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs)
{
return new MarginLayoutParams(getContext(), attrs);
}
@Override
protected LayoutParams generateDefaultLayoutParams()
{
return new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
}
Declare a custom attribute, in values/attrs.xml
<declare-styleable name="PriorityLayoutHorizontal">
<attr name="priorityHigh" format="reference"/>
</declare-styleable>
Using this layout, you can give high priority to the Views that should be visible in the layout definitely. Remaining one TextView will be drawn based on the remaining width in the screen.
To use this layout:
<com.example.component.PriorityLayoutHorizontal
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:priorityHigh="@array/priority_array"
>
<TextView
android:id="@+id/contact_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/your_drawable"
/>
</com.example.component.PriorityLayoutHorizontal>
And define the priority_array in values/arrays.xml
<integer-array name="priority_array">
<item>1</item>
</integer-array>
Here, we have mentioned the priority for the View "1". That is the second TextView in your layout. So the second TextView will be visible always, and the first TextView will be ellipsized.
P.S: this is a generic solution. i.e. this can be used with different Views, and more than 2 Views in a line. You can use this layout, or a custom ViewGroup specific to your usecase could be written.
来源:https://stackoverflow.com/questions/24106885/expand-textview-with-wrap-content-until-the-neighbor-view-reaches-the-end-of-the