Warpzit Warpzit - 6 months ago 122
Android Question

Android animate drop down/up view proper

Okay I'm trying to do a proper slide down animation. The view that slides down should push all views bellow it down in one smooth movement and again when it slides up all the views should follow in one smooth movement.

What I've tryed:
In code:

LinearLayout lin = (LinearLayout)findViewById(R.id.user_list_container);
setLayoutAnimSlidedownfromtop(lin, this);
lin.addView(getLayoutInflater().inflate(R.layout.user_panel,null),0);


And:

public static void setLayoutAnimSlidedownfromtop(ViewGroup panel, Context ctx) {

AnimationSet set = new AnimationSet(true);

Animation animation = new AlphaAnimation(0.0f, 1.0f);
animation.setDuration(100);
set.addAnimation(animation);

animation = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f,
Animation.RELATIVE_TO_SELF, -1.0f, Animation.RELATIVE_TO_SELF, 0.0f
);
animation.setDuration(500);
set.addAnimation(animation);

LayoutAnimationController controller =
new LayoutAnimationController(set, 0.25f);
panel.setLayoutAnimation(controller);

}


My user_panel.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="vertical" >
<ImageView
android:layout_alignParentLeft="true"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="@drawable/icon" />
</LinearLayout>


Top of main xml:

<LinearLayout
android:id="@+id/user_list_container"
android:layout_alignParentTop="true"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<LinearLayout
android:id="@+id/container"
android:layout_below="@+id/user_list_container"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">


The problem with above approach is that when I start the animation first the empty space for the view is created and then the view slides down. I'd like it to slowly push all the other views down instead of doing it in one hard motion.

Answer

So I ended up doing it myself with some help from: http://stackoverflow.com/a/9112691/969325. If it had been android 3.0 (http://developer.android.com/guide/topics/graphics/animation.html) I could have used property animation, but its not so I had to do that myself.

Here is what I ended up with:

import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Transformation;

/**
 * Class for handling collapse and expand animations.
 * @author Esben Gaarsmand
 *
 */
public class ExpandCollapseAnimation extends Animation {
    private View mAnimatedView;
    private int mEndHeight;
    private int mType;

    /**
     * Initializes expand collapse animation, has two types, collapse (1) and expand (0).
     * @param view The view to animate
     * @param duration
     * @param type The type of animation: 0 will expand from gone and 0 size to visible and layout size defined in xml. 
     * 1 will collapse view and set to gone
     */
    public ExpandCollapseAnimation(View view, int duration, int type) {
        setDuration(duration);
        mAnimatedView = view;
        mEndHeight = mAnimatedView.getLayoutParams().height;
        mType = type;
        if(mType == 0) {
            mAnimatedView.getLayoutParams().height = 0;
            mAnimatedView.setVisibility(View.VISIBLE);
        }
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        super.applyTransformation(interpolatedTime, t);
        if (interpolatedTime < 1.0f) {
            if(mType == 0) {
                mAnimatedView.getLayoutParams().height = (int) (mEndHeight * interpolatedTime);
            } else {
                mAnimatedView.getLayoutParams().height = mEndHeight - (int) (mEndHeight * interpolatedTime);
            }
            mAnimatedView.requestLayout();
        } else {
            if(mType == 0) {
                mAnimatedView.getLayoutParams().height = mEndHeight;
                mAnimatedView.requestLayout();
            } else {
                mAnimatedView.getLayoutParams().height = 0;
                mAnimatedView.setVisibility(View.GONE);
                mAnimatedView.requestLayout();
                mAnimatedView.getLayoutParams().height = mEndHeight;
            }
        }
    }
}

Example ussage:

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class AnimationTestActivity extends Activity {
    private boolean mActive = false;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        final Button animatedButton = (Button) findViewById(R.id.animatedButton);

        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                ExpandCollapseAnimation animation = null;
                if(mActive) {
                    animation = new ExpandCollapseAnimation(animatedButton, 1000, 1);
                    mActive = false;
                } else {
                    animation = new ExpandCollapseAnimation(animatedButton, 1000, 0);
                    mActive = true;
                }
                animatedButton.startAnimation(animation);
            }
        });
    }
}

xml:

    <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
    <Button
        android:id="@+id/animatedButton"
        android:visibility="gone"
        android:layout_width="fill_parent"
        android:layout_height="50dp"
        android:text="@string/hello"/>
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />
    <Button
        android:id="@+id/button"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello"/>
</LinearLayout>

Edit:

Measure wrap_content height:

So in order to get this to work for wrap_content I measured the height of the view before I start the animation and then use this measured height as the actual height. Bellow is code for measuring height of the view and set this as the new height (I assume the view uses screen width, change according to your own needs):

/**
 * This methode can be used to calculate the height and set it for views with wrap_content as height. 
 * This should be done before ExpandCollapseAnimation is created.
 * @param activity
 * @param view
 */
public static void setHeightForWrapContent(Activity activity, View view) {
    DisplayMetrics metrics = new DisplayMetrics();
    activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);

    int screenWidth = metrics.widthPixels;

    int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
    int widthMeasureSpec = MeasureSpec.makeMeasureSpec(screenWidth, MeasureSpec.EXACTLY);

    view.measure(widthMeasureSpec, heightMeasureSpec);
    int height = view.getMeasuredHeight();
    view.getLayoutParams().height = height;
}
Comments