6

What i've tried to far:

  1. Calling measure()

    tv.measure(0, 0);
    int height = tv.getMeasuredHeight();
    
  2. Calling measure() with specified sizes/modes

    int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(99999, View.MeasureSpec.AT_MOST);
    int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
    tv.measure(widthMeasureSpec, heightMeasureSpec);
    int height = tv.getMeasuredHeight();
    
  3. Calling getTextBounds()

    Rect bounds = new Rect();
    tv.getPaint().getTextBounds(tv.getText().toString(), 0, tv.getText().length(), bounds);
    int height = bounds.height();
    
  4. Calling measure() and then calling getTextBounds()

  5. Calling getLineCount() * getLineHeight()

None seem to work. They all return incorrect values (container view gets incorrect height - it's either too small or too large)

Ideas on how to calculate this simple thing??

Shai
  • 25,159
  • 9
  • 44
  • 67

4 Answers4

5

You need to specify the available width so the height can be properly calculated.

Note: In cases where you just need to get the height of a view that is already drawn, use ViewTreeObserver. See this question for a thing to consider in that case.

This is why I do, in a piece of code where I want to scale a view from hidden to its full necessary height:

int availableWidth = getParentWidth(viewToScale);

int widthSpec = View.MeasureSpec.makeMeasureSpec(availableWidth, View.MeasureSpec.AT_MOST);
int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);

viewToScale.measure(widthSpec, heightSpec);
int measuredHeight = viewToScale.getMeasuredHeight();
// Offtopic: Now I animate the height from 1 to measuredHeight (0 doesn't work)

You may pass the availableWidth yourself, but I calculate it from the parent:

private int getParentWidth(View viewToScale)
{
    final ViewParent parent = viewToScale.getParent();
    if (parent instanceof View) {
        final int parentWidth = ((View) parent).getWidth();
        if (parentWidth > 0) {
            return parentWidth;
        }
    }

    throw new IllegalStateException("View to scale must have parent with measured width");
}
Community
  • 1
  • 1
Ferran Maylinch
  • 10,919
  • 16
  • 85
  • 100
  • Works like a charm if you call the above code (upper snippet) in a Runnable sumbitted to View.post(). – xmjx Jul 30 '21 at 19:45
1

Where are you calling those methods? The best way to do what you are trying to do is to use ViewTreeObserver and add onPreDrawListener. Take a look at this i think it will help

Community
  • 1
  • 1
EE66
  • 4,601
  • 1
  • 17
  • 20
  • What if I have 8 textviews? do I hadd to add a onPreDrawListener for each one of them? – Shai Jun 02 '15 at 08:45
  • That is souly depends on the code and design. But if the design is ok u can do it with one, but dont forget to remove it so it wont get called again every time the View is veing drawn. – EE66 Jun 02 '15 at 08:48
1

What you can do here is get ViewTreeObserver associated with this TextView and add OnGlobalLayoutListener to it:

final ViewTreeObserver vto = textView.getViewTreeObserver();

vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        // textView dimensions are calculated at this stage, but textView
        // isn't rendered yet. Do what you need to do and remove OnGlobalLayoutListener
        // after

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
            textView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
        } else {
            textView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
        }
    }
}
aga
  • 27,954
  • 13
  • 86
  • 121
  • when I add this, onGlobalLayout is being called 4 times and only gets the correct height on the 1st time. How will I know which call to rely on? – Shai Jun 02 '15 at 08:52
  • If it's called 4 times it means your view hierarchy goes through 4 layout/measure passes, probaby because you have some nested layouts and/or layout weights. Does your TextView have correct dimensions when it's rendered on the screen? – aga Jun 02 '15 at 09:00
  • Yes. This is actually just a 'nasty' workaround for something else. I need [dynamic height](http://stackoverflow.com/questions/26649406/nested-recycler-view-height-doesnt-wrap-its-content) RecyclerView cells and I couldn't get it to work. This hack does work, but only if I get the right textview dimensions (which I currently fail to do so..). Your solution is OK but it fails to work on my side, as I need the height to be calculated immediately once the view has been created... – Shai Jun 02 '15 at 09:05
-1

I had a similar problem recently, trying to measure the height of a ViewGroup containing several multiline TextView.

What worked for me was to measure() the TextView, then add (lineCount-1)*lineHeight to its measuredHeight.

Here is the code to measure only one TextView :

private int getMeasuredHeight(TextView textView) {
    textView.measure(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
    int height = textView.getMeasuredHeight();
        height += (textView.getLineCount()-1) * textView.getLineHeight();
    return height;
}

And in the case of a ViewGroup with many TextViews, here is my code :

private int getMeasuredHeight(ViewGroup viewGroup) {
    viewGroup.measure(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
    int height = viewGroup.getMeasuredHeight();
    for(TextView textView : findAllTextView(viewGroup)) {
        height += (textView.getLineCount()-1) * textView.getLineHeight();
    }
    return height;
}

private List<TextView> findAllTextView(ViewGroup v) {
    List<TextView> result = new ArrayList<>();
    for (int i = 0; i < v.getChildCount(); i++) {
        Object child = v.getChildAt(i);
        if (child instanceof TextView)
            result.add((TextView) child);
        else if (child instanceof ViewGroup)
            for(TextView tv: findAllTextView((ViewGroup) child))
                result.add(tv);
    }
    return result;
}
Gabriel Morin
  • 2,100
  • 1
  • 14
  • 26
  • I did the same mistake. You don't pass `LayoutParams.XXX` to `View.measure()` but values calculated using `View.MeasureSpec.makeMeasureSpec()`. – Ferran Maylinch Feb 18 '16 at 11:09