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

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

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