Android TextView 文本截断优先级或压缩阻力

17

我在屏幕上横向布置了3个视图(固定大小的图片和两个单行文本视图:leftTextViewrightTextView),我试图让rightTextView紧贴着leftTextView,但如果两个标签的宽度超过屏幕大小,则截断leftTextiew

期望功能示例:

|img|leftText|rightText|                ||(end of screen)
|img|leftTextMedium|rightText|          ||
|img|leftTextTooLongSoTrunc...|rightText||

实际上正在发生的事情:

|img|leftText|rightText|                ||(end of screen)
|img|leftTextPrettyLongButNotHuge|rightT||
|img|leftTextWhichIsIncrediblyLongBlahBl||

如果两个文本视图的大小都超过了视图的宽度,那么即使我在leftTextView上设置了android:ellipsize="end"rightTextView也可能被挤压到非常小的大小以腾出空间。

有没有一种方法可以使leftTextView具有“截断优先级”,即如果它会导致其他视图无法适应,则应该将其截断?或者,我可以将rightTextView设置为“压缩阻力”,以防止它被挤压以腾出空间给leftTextView?以下是我的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>

我尝试过的事情:

  1. leftTextView中添加toStartOf / toLeftOfrightTextView(导致应用程序崩溃)。
  2. 与其将rightTextView对齐到leftTextView的末尾/右侧,不如将其翻转并将leftTextView对齐到rightTextView的开始/左侧。 这会导致rightTextView锚定在屏幕末尾,而不是直接在leftTextView的右侧。 以下是最终效果的示例:

    |img|leftText                 |rightText||
    |img|leftTextMedium           |rightText||
    |img|leftTextTooLongSoTrunc...|rightText||
    
  3. 即使我设置android:minWidth="25dp"以确保至少部分 rightTextView 是可见的(我不想这样做,因为这个文本长度是可变的),它仍然会压缩宽度以腾出leftTextView 的空间。

  4. 根据Emanuel Moecklin的回答,我尝试将其包装在一个LinearLayout中,并在leftTextView上设置权重。结果在视觉上与我在#2中尝试的相同。

为了辩护我标记重复问题的行为,当我提出我的问题时,那个其他问题并没有得到答案。此外,它的标题很糟糕,这可能是我一开始没有意识到它存在的原因。 - Stonz2
4个回答

17

您可以通过 TableLayoutshrinkColumns属性来实现此布局。

<?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>

enter image description here


这个能用于垂直布局吗?我需要TableRow中的项目垂直对齐,并设置android:shrinkColumns="0"来缩小第一个视图。 - Vlad
设置 android:shrinkColumns="0" 运行正常。 - woxingxiao
我该如何使用ConstraintLayout来实现这个? - jmarkstar

6

这是我的工作内容:

<?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>

这里的android:layout_weight="1"起到了“技巧”的作用。然而,文档对此并不十分清晰:

例如,如果有三个文本字段,其中两个声明了权重为1,而另一个没有给出权重,则没有权重的第三个文本字段将不会增长,只会占据其内容所需的区域。

(https://developer.android.com/guide/topics/ui/layout/linear.html#Weight)

它没有详细说明当没有足够的空间来填充所有视图时的情况。文档中提到的一种解释是,没有权重的字段(在这种情况下为rightTextView)将占用其内容所需的区域,而具有权重的字段(在这种情况下为leftTextView)将“增长”(如负增长),这正是发生的情况。

1
虽然权重允许您提到的“负增长”,但它也会在不需要时扩展字段以填充宽度。这最终导致rightTextView被固定在父视图的右侧/末端。从功能上看,它与我在问题中尝试的第2种情况相同。 - Stonz2
@Emanuel Moecklin 如果我使用RelativeLayout而不是LinearLayout会怎样? - AJW
@Stonz2 这也是我的测试结果发现的。 - new

1
我认为你需要制作一个自定义布局。这对我所做的示例非常有效,满足你上面提到的所有要求,但你需要在代码中添加更多的数学计算来考虑布局边距和填充。在这里发生的基本思想是我子类化了LinearLayout,在onMeasure pass中,我检查右侧视图是否有选项可以尽可能占用空间。请注意,我必须重新测量右侧视图,而不仅仅是获取测量宽度,因为线性布局不会给予足够的空间。一旦你知道右侧视图占用了多少空间,就检查一下中间视图(称为leftView...抱歉)是否占用了比你想要的更多的空间。如果是的话,就把剩下的空间给它。根据你的要求,它需要在RecyclerView中...我知道你说你想要一个纯xml解决方案,但如果你直接自己处理,你会获得更好的性能,因为xml 1.可能是不可能的,2.将需要比直接处理更多的计算。
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布局
<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>

enter image description here


谢谢你的答案-我试了一下,效果很好。但是nshmura的方法也可以,而且完全基于XML,不需要使用子类化。 - Stonz2

0

只需在左侧textView中使用android:maxWidth属性。这样,您的左视图将永远不会超过该限制,并且始终会为右视图留出空间。

<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>

Preview of layout

然而,解决方案的限制在于您必须在maxWidth属性中提供固定值。 因此,在不同的屏幕大小上显示不同的外观。 虽然,在大多数情况下,您不会遇到问题。
此外,对于在单行中显示长的textView,“android:ellipsize =”marquee“”是一个很好的选择。 因为它会水平滚动您的视图。 但不要忘记在代码中编写textView.setSelected(true)以使其工作。 Detail

1
由于Android拥有如此多不同的屏幕尺寸(加上纵向和横向),固定宽度绝对不是一个理想的方法。 - Stonz2
我已经说过,当您知道视图的某个固定宽度大小时,这将起作用。否则,您可以像@whizzle建议的那样以编程方式完成。 - Shaishav Jogani

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接