Cómo animar View.setVisibility (GONE)

Quiero hacer una Animation para cuando una View obtiene su visibilidad establecida en GONE . En lugar de simplemente desaparecer, la View debería ‘colapsar’. Intenté esto con una ScaleAnimation pero luego la View se colapsó, pero el diseño solo cambiará el tamaño de su espacio después (o antes) de que la Animation detenga (o comience).

¿Cómo puedo hacer la Animation para que, mientras se anima, la View inferior s permanezca directamente debajo del contenido, en lugar de tener un espacio en blanco?

No parece haber una manera fácil de hacerlo a través de la API, porque la animación simplemente cambia la matriz de representación de la vista, no el tamaño real. Pero podemos establecer un margen negativo para engañar a LinearLayout y hacerle pensar que la vista se está haciendo más pequeña.

Así que recomendaría crear su propia clase de animación, basada en ScaleAnimation, y anular el método “applyTransformation” para establecer nuevos márgenes y actualizar el diseño. Me gusta esto…

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

Se aplica la advertencia habitual: como estamos anulando un método protegido (applyTransformation), no está garantizado que funcione en futuras versiones de Android.

Coloque la vista en un diseño si no es así y configure android:animateLayoutChanges="true" para ese diseño.

Usé la misma técnica que Andy aquí presentó. Escribí mi propia clase de Animación para eso, que anima el valor del margen, haciendo que el efecto del elemento desaparezca / aparezca. Se parece a esto:

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

Tengo un ejemplo completo que funciona en mi blog http://udinic.wordpress.com/2011/09/03/expanding-listview-items/

Usé la misma técnica que Andy aquí, y la refiné para que se pueda usar para expandir y colapsar sin fallas, también usando una técnica que se describe aquí: 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(); } }