如何使 View.setVisiability (GONE)动画化

我想做一个 Animation当一个 View得到它的可见性设置为 GONEView应该“崩溃”,而不是消失。我尝试这与一个 ScaleAnimation,但然后的 View是崩溃,但布局将只调整它的空间后(或之前)的 Animation停止(或启动)。

我如何才能使 Animation,使,而动画,较低的 View将留在正下方的内容,而不是有一个空白的空间?

65493 次浏览

There doesn't seem to be an easy way to do this through the API, because the animation just changes the rendering matrix of the view, not the actual size. But we can set a negative margin to fool LinearLayout into thinking that the view is getting smaller.

So I'd recommend creating your own Animation class, based on ScaleAnimation, and overriding the "applyTransformation" method to set new margins and update the layout. Like this...

public class Q2634073 extends Activity implements OnClickListener {


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.q2634073);
findViewById(R.id.item1).setOnClickListener(this);
}


@Override
public void onClick(View view) {
view.startAnimation(new MyScaler(1.0f, 1.0f, 1.0f, 0.0f, 500, view, true));
}


public class MyScaler extends ScaleAnimation {


private View mView;


private LayoutParams mLayoutParams;


private int mMarginBottomFromY, mMarginBottomToY;


private boolean mVanishAfter = false;


public MyScaler(float fromX, float toX, float fromY, float toY, int duration, View view,
boolean vanishAfter) {
super(fromX, toX, fromY, toY);
setDuration(duration);
mView = view;
mVanishAfter = vanishAfter;
mLayoutParams = (LayoutParams) view.getLayoutParams();
int height = mView.getHeight();
mMarginBottomFromY = (int) (height * fromY) + mLayoutParams.bottomMargin - height;
mMarginBottomToY = (int) (0 - ((height * toY) + mLayoutParams.bottomMargin)) - height;
}


@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
if (interpolatedTime < 1.0f) {
int newMarginBottom = mMarginBottomFromY
+ (int) ((mMarginBottomToY - mMarginBottomFromY) * interpolatedTime);
mLayoutParams.setMargins(mLayoutParams.leftMargin, mLayoutParams.topMargin,
mLayoutParams.rightMargin, newMarginBottom);
mView.getParent().requestLayout();
} else if (mVanishAfter) {
mView.setVisibility(View.GONE);
}
}


}


}

The usual caveat applies: because we are overriding a protected method (applyTransformation), this is not guaranteed to work in future versions of Android.

I used the same technique as Andy here has presented. I wrote my own Animation class for that, that animate the margin's value, causing the effect of the item to disappear/appear. It looks like this:

public class ExpandAnimation extends Animation {


// Initializations...


@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);


if (interpolatedTime < 1.0f) {


// Calculating the new bottom margin, and setting it
mViewLayoutParams.bottomMargin = mMarginStart
+ (int) ((mMarginEnd - mMarginStart) * interpolatedTime);


// Invalidating the layout, making us seeing the changes we made
mAnimatedView.requestLayout();
}
}
}

I have a full example that works on my blog post http://udinic.wordpress.com/2011/09/03/expanding-listview-items/

I used the same technique as Andy here, and refined it so that it can be used for expanding and collapsing without glitches, also using a technique described here: https://stackoverflow.com/a/11426510/1317564

import android.view.View;
import android.view.ViewTreeObserver;
import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;
import android.widget.LinearLayout;


class LinearLayoutVerticalScaleAnimation extends ScaleAnimation {
private final LinearLayout view;
private final LinearLayout.LayoutParams layoutParams;


private final float beginY;
private final float endY;
private final int originalBottomMargin;


private int expandedHeight;
private boolean marginsInitialized = false;
private int marginBottomBegin;
private int marginBottomEnd;


private ViewTreeObserver.OnPreDrawListener preDrawListener;


LinearLayoutVerticalScaleAnimation(float beginY, float endY,
LinearLayout linearLayout) {
super(1f, 1f, beginY, endY);


this.view = linearLayout;
this.layoutParams = (LinearLayout.LayoutParams) linearLayout.getLayoutParams();


this.beginY = beginY;
this.endY = endY;
this.originalBottomMargin = layoutParams.bottomMargin;


if (view.getHeight() != 0) {
expandedHeight = view.getHeight();
initializeMargins();
}
}


private void initializeMargins() {
final int beginHeight = (int) (expandedHeight * beginY);
final int endHeight = (int) (expandedHeight * endY);


marginBottomBegin = beginHeight + originalBottomMargin - expandedHeight;
marginBottomEnd = endHeight + originalBottomMargin - expandedHeight;
marginsInitialized = true;
}


@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);


if (!marginsInitialized && preDrawListener == null) {
// To avoid glitches, don't draw until we've initialized everything.
preDrawListener = new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
if (view.getHeight() != 0) {
expandedHeight = view.getHeight();
initializeMargins();
adjustViewBounds(0f);
view.getViewTreeObserver().removeOnPreDrawListener(this);
}


return false;
}
};


view.getViewTreeObserver().addOnPreDrawListener(preDrawListener);
}


if (interpolatedTime < 1.0f && view.getVisibility() != View.VISIBLE) {
view.setVisibility(View.VISIBLE);
}


if (marginsInitialized) {
if (interpolatedTime < 1.0f) {
adjustViewBounds(interpolatedTime);
} else if (endY <= 0f && view.getVisibility() != View.GONE) {
view.setVisibility(View.GONE);
}
}
}


private void adjustViewBounds(float interpolatedTime) {
layoutParams.bottomMargin =
marginBottomBegin + (int) ((marginBottomEnd - marginBottomBegin) * interpolatedTime);


view.getParent().requestLayout();
}
}

Put the view in a layout if it's not and set android:animateLayoutChanges="true" for that layout.