16

Currently, I have a layout which looks like this. It contains.

  • Title text view, and Price text view, which is visible always.
  • Description Text View, which can be visible or gone, depending expanding or collapsing.

Collapsing (During app startup)

enter image description here

Expanding (When user taps on it)

enter image description here

I want to have some nice animation around it. So, I referred to https://stackoverflow.com/a/13381228/72437

One of the key element, is to know the exact height of Description Text View, even before it is visible.

However, I realize a few type of code. They are't accurate

// v is description text view.
v.measure(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
final int targtetHeight = v.getMeasuredHeight();

// v is description text view.
v.measure(MeasureSpec.makeMeasureSpec(LayoutParams.MATCH_PARENT, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(LayoutParams.WRAP_CONTENT, MeasureSpec.EXACTLY));
final int targtetHeight = v.getMeasuredHeight();

This will return value 32. (The correct measured height suppose to be 92). This is the height for first line of text. This ends up my animation is ended at

enter image description here

May I know, what is the correct way to determine the measured height of a view, even before it changed from GONE to VISIBLE?

My layout code is as followed :

        <LinearLayout
            android:clickable="true"
            android:id="@+id/chart_linear_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp"
            android:background="@drawable/dummy"
            android:orientation="vertical">
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_margin="10dp"
                android:orientation="horizontal">
                <TextView
                    android:layout_width="0dp"
                    android:width="0dp"
                    android:layout_weight="0.6"
                    android:layout_height="match_parent"
                    android:gravity="left"
                    android:textSize="20sp" 
                    android:textColor="#ff000000"
                    android:text="Summary chart" />
                <TextView
                    android:id="@+id/chart_price_text_view"
                    android:layout_width="0dp"
                    android:width="0dp"
                    android:layout_weight="0.4"
                    android:layout_height="match_parent"
                    android:gravity="right"
                    android:textSize="20sp" 
                    android:textColor="#ffF76D3C"
                    android:text="$2.99" />

            </LinearLayout>
            <TextView
                android:visibility="gone"
                android:id="@+id/chart_description_text_view"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="10dp"
                android:layout_marginBottom="10dp"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"         
                android:text="@string/currency_exchange_description"
                android:textColor="#ff626262"
                android:textSize="15sp" />                 
        </LinearLayout>
Community
  • 1
  • 1
Cheok Yan Cheng
  • 47,586
  • 132
  • 466
  • 875
  • Unless you know how many lines the text will take up, there isn't a way to measure it before it's measured. A *hack* way to do it might be to set them all to `INVISIBLE` rather than gone at the start, measure, then `GONE` them immediately afterward. If you only have a few views, this *might* not be noticeable to the user. – Geobits Oct 08 '13 at 18:40
  • Do you have any explanation why multiple wrapped line doesn't work? I thought v.measure(... should work (Although it doesn't work right now) – Cheok Yan Cheng Oct 08 '13 at 18:42
  • Not sure, but I think it's just measuring what it expects for a `TextView` in general. It may not yet realize it will need multiple lines. – Geobits Oct 08 '13 at 18:43

2 Answers2

45

Your 2nd code snippet is almost correct, but you need to specify pixel sizes - not FILL_PARENT/MATCH_PARENT. This should work:

v.measure(MeasureSpec.makeMeasureSpec(parentView.getWidth(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(MAX_HEIGHT, MeasureSpec.AT_MOST));
final int targetHeight = v.getMeasuredHeight();

You'll need to have a reference to the ViewGroup that v is a child of to get its width, and define MAX_HEIGHT (or perhaps use the parent View's height?).

Also, you should change the height parameters of the two TextViews that are within the horizontal LinearLayout to wrap_content, as using match_parent here may cause problems. The LinearLayout is set to wrap_content, but the two children don't specify a height.

nmw
  • 6,664
  • 3
  • 31
  • 32
  • That helped me too, but in my case it was not the full story. This is how I finally solved it: http://stackoverflow.com/a/36184454/854396 – Hermann Klecker Mar 23 '16 at 17:08
3

I accomplished this for my accordion component. I was facing the exact same issue, of expanding my accordion which required the 'destination' height, that is, the height that it would take after the expansion. This returns the correct height:

/***
 * This function returns the actual height the layout. The getHeight() function returns the current height which might be zero if
 * the layout's visibility is GONE
 * @param layout
 * @return
 */
public static int getFullHeight(ViewGroup layout) {
    int specWidth = View.MeasureSpec.makeMeasureSpec(0 /* any */, View.MeasureSpec.UNSPECIFIED);
    int specHeight = View.MeasureSpec.makeMeasureSpec(0 /* any */, View.MeasureSpec.UNSPECIFIED);


    layout.measure(specWidth,specHeight);
    int totalHeight = 0;//layout.getMeasuredHeight();
    int initialVisibility = layout.getVisibility();
    layout.setVisibility(View.VISIBLE);
    int numberOfChildren = layout.getChildCount();
    for(int i = 0;i<numberOfChildren;i++) {
        View child = layout.getChildAt(i);
        if(child instanceof ViewGroup) {
            totalHeight+=getFullHeight((ViewGroup)child);
        }else {
            int desiredWidth = View.MeasureSpec.makeMeasureSpec(layout.getWidth(),
                    View.MeasureSpec.AT_MOST);
            child.measure(desiredWidth, View.MeasureSpec.UNSPECIFIED);
            totalHeight+=child.getMeasuredHeight();
        }

    }
    layout.setVisibility(initialVisibility);
    return totalHeight;
}

You can take a look at the accordion component and the animation on GitHub: Android Accordion View

Riya Gayasen
  • 81
  • 1
  • 8