Ellipsize not working properly for a multiline TextView with an arbitrary maximum height
Asked Answered
L

2

25

I have a TextView with an unknown maximum height, which is dependent on the device's DPI/screen resolution. So, for instance, on and MDPI device this maximum height makes it possible to show only 2 lines at a time, a value that can be increased up to an undefined number.

My issue is related with the ellipsize functionality. Let's suppose that a certain device allows for 4 lines to be displayed. If I manually set the maximum number of lines, like this...

<TextView
    android:id="@+id/some_id"
    android:layout_width="fill_parent"
    android:layout_height="0dip"
    android:ellipsize="end" 
    android:maxLines="4"
    android:singleLine="false"
    android:layout_weight="1"
    android:gravity="center_vertical"
    android:text="This is some really really really really really long text"
    android:textSize="15sp" />

...everything works OK. If the text doesn't fit properly, then the ellipsis are added at the end of the fourth line, like this:

This is some
really really
really really
really long...

But I'd rather not set the number of lines as a static variable, as I would prefer to include support for any combination of DPI/screen resolution. So if I remove maxLines the ellipsis is no longer correctly shown at line four, showing instead an incomplete portion of text:

This is some
really really
really really
really long

If I slightly increase the TextView size, I can see that the rest of the text is still being drawn "behind" the other Views. Setting the variable maxHeight doesn't seem to work either.

I really can't seem to find a solution for this issue. Any ideas? If it helps, I'm working only with Android v4.0.3 and up (API level 15).

Labellum answered 5/1, 2013 at 16:26 Comment(0)
C
37

Calculate how many lines fit into the TextView with TextView#getHeight() and TextView#getLineHeight(). Then call TextView#setMaxLines().

ViewTreeObserver observer = textView.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        int maxLines = (int) textView.getHeight()
                / textView.getLineHeight();
        textView.setMaxLines(maxLines);
        textView.getViewTreeObserver().removeGlobalOnLayoutListener(
                this);
    }
});
Cantal answered 5/1, 2013 at 19:9 Comment(4)
Note: the line height may vary! And therefor the method getLineHeight wont always work as expected. This could happen for example when using spannables to make the font size bigger in a specific part of the text.Trainbearer
Just adding a link to the documentation for what Rolf added.Diapause
This answer works well up to API 27. Since API 28 and 29, there is (by default) more spacing between lines. For example, while textView.getLineHeight() returns 55 (I use Pixel 2 AVD), the actual height is 66 (or 67), including the extra space. So far, the only way I can get this number (66) is via textView.getLineBounds().Gynaecocracy
This is old, but goldSomebody
G
6

The accepted answer works well up to API 27. However, since API 28, if the line height was not set (by one of the follow methods), by default Android adds extra spacing between lines, but not after the last line.

  1. Set attribute android:lineHeight=... (documentation) in your layout XML
  2. Calls textView.setLineHeight(...) in your source code.

To find out the new line height for API 28 and above, I used textView.getLineBounds().

Kotlin

val observer = textView?.viewTreeObserver
observer?.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
    override fun onGlobalLayout() {
        textView?.let { view ->
            val lineHeight: Int
            lineHeight = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                val bounds = Rect()
                textView.getLineBounds(0, bounds)
                bounds.bottom - bounds.top
            } else {
                textView.lineHeight
            }
            val maxLines = textView.height / lineHeight
            textView.maxLines = maxLines
            textView.viewTreeObserver.removeOnGlobalLayoutListener(this)
        }
    }
})

Android Java

ViewTreeObserver observer = textView.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        int lineHeight = textView.getLineHeight();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            Rect bounds = new Rect();
            textView.getLineBounds(0, bounds);
            lineHeight = bounds.bottom - bounds.top;
        }
        int maxLines = (int) textView.getHeight() / lineHeight;
        textView.setMaxLines(maxLines);
        textView.getViewTreeObserver().removeGlobalOnLayoutListener(
            this);
    }
});
Gynaecocracy answered 22/10, 2019 at 1:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.