2012-02-12 15 views
16

Bien, estoy intentando hacer una animación de deslizamiento hacia abajo adecuada. La vista que se desliza hacia abajo debe empujar todas las vistas hacia abajo en un movimiento uniforme y, nuevamente, cuando se desliza, todas las vistas deben seguir en un movimiento suave.Vista ascendente/descendente de animaciones de Android adecuada

Lo que he tryed: En código:

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

Y:

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

} 

Mi 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> 

Arriba XML principal:

<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"> 

El problema con el enfoque anterior es que cuando comienzo la animación primero se crea el espacio vacío para la vista y luego la vista se desliza hacia abajo. Me gustaría que bajara lentamente todas las otras vistas en lugar de hacerlo en un solo movimiento.

+1

Creo que necesitas ver la clase 'LayoutTransition'. Proporciona mecanismos para animar la adición/eliminación de vistas hacia/desde el diseño. –

Respuesta

56

Así que terminé haciéndolo yo mismo con la ayuda de: https://stackoverflow.com/a/9112691/969325. Si hubiera sido Android 3.0 (http://developer.android.com/guide/topics/graphics/animation.html) podría haber usado la animación de la propiedad, pero no es así, tuve que hacer eso yo mismo.

Esto es lo que terminó con:

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

Ejemplo 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> 

Editar:

medir la altura wrap_content:

Así que con el fin de conseguir que esto funcione para wrap_content Medí la altura de la vista antes de que comience la animación y luego utilizar esta altura, medida como la altura real. Abajo es el código para la medición de la altura de la vista y establecer esto como la nueva altura (supongo que la vista utiliza ancho de la pantalla, cambie de acuerdo a sus propias necesidades):

/** 
* 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; 
} 
+0

Ok para elementos de altura forcer. Pero desafortunadamente este método no funciona para los elementos de contenido wrap :( –

+0

@bixi en realidad lo hace, solo tiene que calcular la altura para el contenido del wrap. Es factible (lo hizo pero no puede encontrar la referencia quick = /, dime si lo necesita) – Warpzit

+0

gran ejemplo! también puede reutilizar quizá View.VISIBLE y View.GONE para indicar el tipo (resultado animación) – Bostone

5

Gracias Warpzit! Esa fue una respuesta muy útil. En mi caso, solo estaba tratando de animar vistas con altura que era wrap_content. Intenté una sugerencia de dos líneas, pero no funcionó en mi caso. (Que no pasamos mucho tiempo persiguiendo por qué.) Terminé usando una forma ligeramente modificada de Warpzit de ExpandCollapseAnimation con su método estático para determinar la altura de la vista

En poco más de detalle:

  1. Incluí su método estático setHeightForWrapContent() en la clase ExpandCollapseAnimation.
  2. Llamo al setHeightForWrapContent() en el constructor ExpandCollapseAnimation para determinar correctamente el alto de la vista.Para hacer esto, tengo que pasar la actividad con el constructor.
  3. En el método applyTransformation(), cuando la vista es finalmente reducido a cero altura, vuelvo la altura de la vista posterior a wrap_content. Si no lo hace y cambia el contenido de la vista más tarde, cuando lo expanda, la vista se ampliará a la altura determinada previamente.

el código está aquí:

public class ExpandCollapseAnimation extends Animation { 
    private View mAnimatedView; 
    private int mEndHeight; 
    private int mType; 

    public ExpandCollapseAnimation(View view, int duration, int type, Activity activity) { 
     setDuration(duration); 
     mAnimatedView = view; 

     setHeightForWrapContent(activity, 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 = LayoutParams.WRAP_CONTENT;  // Return to wrap 
      } 
     } 
    } 

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

Gracias de nuevo, Warpzit!

+0

¡Esto funciona, impresionante! –

Cuestiones relacionadas