为什么 TextView 在多行模式下有末尾内边距?

21
如果您有一个TextView,其layout_width="wrap_content",并且它必须换行才能容纳文本,则它将调整其宽度以使用所有可用空间(考虑边距等)。但是为什么视图末尾有填充?我只是告诉它wrap_content,所以它应该包裹内容!这似乎是一个错误,在股票Messenger应用程序的聊天UI中可见。(图片来自我的应用程序。但那个额外的空间绝对不在9 patch中。)
有什么解决方法吗?
更新:回答者/评论者没有理解重点。也许我上传的图片会让人感到困惑,因为它是从我的应用程序中进行样式设置的。任何TextView都会出现问题,您可以通过设置背景来查看视图边界将不再紧密。我上传了另一张图片。以下是图像中TextView的XML:
        <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginEnd="20dp"
    android:layout_marginStart="20dp"
    android:background="#dddddd"
    android:text="This doesn't wrap"
    android:layout_marginTop="20dp"
    android:layout_marginBottom="20dp"
    android:layout_gravity="center_horizontal"
    />
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginEnd="20dp"
    android:layout_marginStart="20dp"
    android:layout_gravity="center_horizontal"
    android:background="#dddddd"
    android:text="This wraps and look, the bounds does not fit tight against the right edge of text"
    />

enter image description here


也许这个词不适合?也许你的9patch有一些内部背景? - Blackbelt
我更新了我的答案,现在应该涵盖了你的观点。 - Yves Delerm
4个回答

10

一开始看到您的帖子时,我认为问题是由于标准的Android TextView在其基本样式中定义了一些默认填充。如果想要删除它,可以尝试以下方法:

android:paddingEnd="0dp"

或者

android:paddingRight="0dp"

随着您的帖子更新,我了解到您的问题不是来自填充,而是来自换行。当需要显示多行时,Android TextView 会使用整个可用宽度。
正如此帖子中所述,目前没有标准解决方案,您需要自定义文本视图以在填充后修复其宽度。
覆盖您的textViewonMeasure方法,如下所示,应该可以解决问题(受“sky”答案的启发):
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    int specModeW = MeasureSpec.getMode(widthMeasureSpec);
    if (specModeW != MeasureSpec.EXACTLY) {
        Layout layout = getLayout();
        int linesCount = layout.getLineCount();
        if (linesCount > 1) {
            float textRealMaxWidth = 0;
            for (int n = 0; n < linesCount; ++n) {
                textRealMaxWidth = Math.max(textRealMaxWidth, layout.getLineWidth(n));
            }
            int w = (int) Math.ceil(textRealMaxWidth);
            if (w < getMeasuredWidth()) {
                super.onMeasure(MeasureSpec.makeMeasureSpec(w, MeasureSpec.AT_MOST),
                        heightMeasureSpec);
            }
        }
    }
}

1
谢谢,我尝试搜索相同的问题,但没有找到。肯定是同样的问题。很遗憾看到三年后,我们仍然需要为此做一个解决方法。 - androidguy

9

我发现所选答案很有帮助,但它没有考虑到填充。我将所选答案与此帖子的答案结合起来,得出了一个可以使用填充的视图。另外,那个帖子的答案有一个缺陷,即视图有时会在末尾被几个像素截断。

public class TightTextView extends TextView {
  public TightTextView(Context context) {
    super(context);
  }

  public TightTextView(Context context, AttributeSet attrs) {
    super(context, attrs);
  }

  public TightTextView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
  }

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

    int specModeW = MeasureSpec.getMode(widthMeasureSpec);
    if (specModeW != MeasureSpec.EXACTLY) {
      Layout layout = getLayout();
      if (layout != null) {
        int w = (int) Math.ceil(getMaxLineWidth(layout)) + getCompoundPaddingLeft() + getCompoundPaddingRight();
        if (w < getMeasuredWidth()) {
          super.onMeasure(MeasureSpec.makeMeasureSpec(w, MeasureSpec.AT_MOST),
          heightMeasureSpec);
        }
      }
    }
  }

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

感谢您帮助改进解决方案,这绝对是一个令人烦恼的问题。 - androidguy
1
如果TightTextView位于ConstraintLayout内部,则其工作可能是不稳定的,这可能取决于TightTextView元素的属性。 - Gregory

3

最近我在为应用程序开发聊天气泡视图时遇到了类似的问题,因此我使用了被接受的解决方案的想法以及@hoffware的改进,并在Kotlin中重新实现了它们。

import android.content.Context
import android.util.AttributeSet
import android.view.View.MeasureSpec.*
import androidx.appcompat.widget.AppCompatTextView
import kotlin.math.ceil

class TightTextView
@JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = android.R.attr.textViewStyle
) : AppCompatTextView(context, attrs, defStyleAttr) {

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)

        val lineCount = layout.lineCount
        if (lineCount > 1 && getMode(widthMeasureSpec) != EXACTLY) {
            val textWidth = (0 until lineCount).maxOf(layout::getLineWidth)
            val padding = compoundPaddingLeft + compoundPaddingRight
            val w = ceil(textWidth).toInt() + padding

            if (w < measuredWidth) {
                val newWidthMeasureSpec = makeMeasureSpec(w, AT_MOST)
                super.onMeasure(newWidthMeasureSpec, heightMeasureSpec)
            }
        }
    }
}

0
我相信这是因为“against”一词不适合放在“fit”和右边缘之间。如果您想测试一下,请将文本更改为一堆|,每个之间只有一个空格。

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