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

DPR
  • 770
  • 1
  • 11
  • 29

2 Answers2

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);
    }
});
Matthias Robbers
  • 15,689
  • 6
  • 63
  • 73
  • 2
    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. – Rolf ツ Jun 12 '13 at 15:43
  • Just adding a [link to the documentation](http://developer.android.com/reference/android/widget/TextView.html#getLineHeight()) for what Rolf added. – yiati Jun 21 '13 at 16:20
  • 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()`. – John Pang Oct 17 '19 at 09:11
  • This is old, but gold – Alann Maulana Jan 27 '22 at 12:49
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);
    }
});
John Pang
  • 2,403
  • 25
  • 25