2010-04-16 20 views
24

Estoy creando algo así como una SlideDrawer pero con la mayoría de las personalizaciones, básicamente la cosa funciona, pero la animación parpadea al final.Android TranslateAnimation se reinicia después de la animación

Para seguir explicando, obtuve una TranslateAnimation luego, después de esta animación, vuelve a la posición original, si configuro setFillAfter, los botones dentro del diseño dejan de funcionar. Si escucho onAnimationEnd y configuro el otro en View.GONE, el diseño parpadea. A juzgar por esto, al final de la animación, la vista vuelve a la posición original antes de que se llame a View.GONE.

Cualquier consejo sería increíble. Gracias

Respuesta

43

Here es el error real relacionada con este tema

Esto, básicamente, establece que el método onAnimationEnd(...) en realidad no funciona bien cuando un AnimationListener se une a una animación

La solución consiste en escuchar la animación eventos en la vista a la que estaban aplicando la animación para Por ejemplo, si inicialmente estabas Colocación del oyente animación a la animación como este

mAnimation.setAnimationListener(new AnimationListener() { 
    @Override 
    public void onAnimationEnd(Animation arg0) { 
         //Functionality here 
    } 

y t uando se aplican a la animación a un ImageView como esto

mImageView.startAnimation(mAnimation); 

Para evitar este problema, debe crear ahora una costumbre ImageView

public Class myImageView extends ImageView { 

y luego reemplazar el método onAnimationEnd de la clase View y proporcionará toda la la funcionalidad existe

@Override 
protected void onAnimationEnd() { 
    super.onAnimationEnd(); 
    //Functionality here 
} 

Esta es la solución adecuada para este problema, proporcione la funcionalidad en el método Over-riden View ->onAnimationEnd(...) en comparación con el método onAnimationEnd(...) del AnimationListener adjunto a la animación.

Esto funciona correctamente y ya no parpadea hacia el final de la animación. Espero que esto ayude

+0

Esto funcionó para mí, muchas gracias. –

+0

Mi placer, Nick. – Soham

+0

¡Gracias, esto funciona como se describe, se desperdicia tiempo antes de encontrar su publicación! – radhoo

1

Deshágase de setFillAfter y simplemente use View.GONE en onAnimationEnd(). Consulte here para obtener una vista personalizada de ejemplo que implementa un panel deslizante con un TranslateAnimation.

+0

-1 el ejemplo no es una respuesta al restablecimiento de la animación. –

+0

@ahmet emrah: Y su prueba de su reclamo es ... ¿qué, exactamente? – CommonsWare

+0

haciendo que la Vista "se haya ido" no es una opción cuando necesitamos esa vista para sucesos sucesivos similares. p.ej. haciendo clic en el botón animado de nuevo, usando el control deslizante animado de nuevo, etc. – radhoo

3

La respuesta de Soham anterior funciona para mí, aunque vale la pena señalar (ya que no fue inmediatamente obvio para mí al leer este hilo) que todavía se puede obtener casi el mismo comportamiento que un oyente de animación configurar un oyente por separado en la vista para ejecutar al final de su Vista onAnimationStart() y onAnimationEnd().

Por ejemplo, si su código tiene que desactivar un botón para la duración de una animación:

Animation a = getAnimation(/* your code */); 
a.setDuration(1000); 
a.setAnimationListener(new AnimationListener() { 
    @Override 
    public void onAnimationStart(Animation arg0) { 
    myButton.setEnabled(false); 
    } 

    @Override 
    public void onAnimationEnd(Animation arg0) { 
    myButton.setEnabled(true); 
    } 
}); 
someView.startAnimation(a); 

Actualmente, no sabe nada de myButton, y me gustaría que siga siendo así.Usted sólo puede crear algún oyente a la clase de vista personalizada que es llamada de la misma manera:

public final class SomeView extends View { 
    // other code 

    public interface RealAnimationListener { 
     public void onAnimationStart(); 
     public void onAnimationEnd(); 
    } 

    private RealAnimationListener mRealAnimationListener; 

    public void setRealAnimationListener(final RealAnimationListener listener) { 
     mRealAnimationListener = listener; 
    } 

    @Override 
    protected void onAnimationStart() { 
     super.onAnimationStart(); 
     if (mRealAnimationListener != null) { 
     mRealAnimationListener.onAnimationStart(); 
     } 
    } 

    @Override 
    protected void onAnimationEnd() { 
     super.onAnimationEnd(); 
     if (mRealAnimationListener != null) { 
     mRealAnimationListener.onAnimationEnd(); 
     } 
    } 
} 

y luego de vuelta en su otro código (probablemente una actividad):

Animation a = getAnimation(/* your code */); 
a.setDuration(1000); 
someView.setRealAnimationListener(new RealAnimationListener() { 
    @Override 
    public void onAnimationStart() { 
    myButton.setEnabled(false); 
    } 

    @Override 
    public void onAnimationEnd() { 
    myButton.setEnabled(true); 
    } 
}); 
someView.startAnimation(a); 

De esta manera se mantiene sus componentes se separaron limpiamente mientras siguen obteniendo un AnimationListener que funciona.

23

Desde la API 11, puede usar el ObjectAnimator, que realmente cambia las propiedades de la vista, es decir, en el caso de una traducción, la vista permanecerá en la posición a la que llega después de la animación.

ObjectAnimator objectAnimator= ObjectAnimator.ofFloat(mContent_container, "translationX", startX, endX); 
objectAnimator.setDuration(1000); 
objectAnimator.start(); 

Más here.

+0

FYI puede usar la misma API con versiones anteriores de Android usando 9oldAndroids lib. http://nineoldandroids.com – Patrick

+0

@Patrick Esto podría ser genial, lo intentaré – 2cupsOfTech

+1

¡gracias!, termina mi lucha de 2 días. – Bhimbim

2

Usando la respuesta de Soham, aquí es una específica ImageView a desvanecerse animaciones:

import android.content.Context; 
import android.util.AttributeSet; 
import android.view.View; 
import android.widget.ImageView; 

/* 
* Custom view to prevent flickering on animation end 
* 
* http://stackoverflow.com/questions/2650351/android-translateanimation-resets-after-animation 
*/ 
public class FadeableImageView extends ImageView { 

public FadeableImageView(Context context) { 
    super(context); 
} 

public FadeableImageView(Context context, AttributeSet attrs) { 
    super(context, attrs); 
} 

public FadeableImageView(Context context, AttributeSet attrs, int defStyle) { 
    super(context, attrs, defStyle); 
} 

@Override 
protected void onAnimationEnd() { 
    super.onAnimationEnd(); 
    this.setVisibility(View.GONE); 
} 
} 

Y aquí es mi código de animación:

protected void startSplash() { 
    final FadeableImageView splash = (FadeableImageView) findViewById(R.id.splash); 

    Animation fadeOut = new AlphaAnimation(1, 0); 
    fadeOut.setDuration(2000); 
    splash.startAnimation(fadeOut); 
} 
+1

Da java.lang.ClassCastException: android.widget.ImageView no se puede convertir en com.animate.FadeableImageView.¿Cuál es el motivo? – Joyson

0

Entonces, estaba buscando la respuesta a esto para mi proyecto Xamarin, pero supongo que también debería aplicarse a Java. La realización que tuve fue que LinearLayout siendo animado SIEMPRE tenía la misma posición (digamos, estaba en x = 100, y == 100) y tus animaciones deberían ser RELATIVAS a esta posición. El ObjectAnimator era definitivamente el camino a seguir, y aquí está mi solución:

En primer lugar, un diseño simple con un poco de texto en la parte superior y una LinearLayout debajo de lo que es el objetivo de la animación ....

<?xml version="1.0" encoding="utf-8"?> 
 
<FrameLayout xmlns:p1="http://schemas.android.com/apk/res/android" 
 
    p1:minWidth="25px" 
 
    p1:minHeight="25px" 
 
    p1:layout_width="match_parent" 
 
    p1:layout_height="match_parent" 
 
    p1:id="@+id/frameLayout1"> 
 
    <TextView 
 
     p1:text="Some text at the top" 
 
     p1:textAppearance="?android:attr/textAppearanceLarge" 
 
     p1:id="@+id/txtSomeTextAtTheTop" 
 
     p1:layout_width="wrap_content" 
 
     p1:layout_height="wrap_content" 
 
     p1:layout_gravity="center_horizontal" /> 
 
    <LinearLayout 
 
     p1:orientation="vertical" 
 
     p1:minWidth="25px" 
 
     p1:minHeight="25px" 
 
     p1:layout_width="wrap_content" 
 
     p1:layout_height="wrap_content" 
 
     p1:id="@+id/linMySlider" 
 
     p1:layout_gravity="center_horizontal|bottom"> 
 
     <LinearLayout 
 
      p1:orientation="horizontal" 
 
      p1:minWidth="25px" 
 
      p1:minHeight="25px" 
 
      p1:layout_width="match_parent" 
 
      p1:layout_height="wrap_content" 
 
      p1:id="@+id/linAlwaysDisplay" 
 
      p1:layout_marginBottom="10px"> 
 
      <TextView 
 
       p1:text="ALWAYS ON DISPLAY" 
 
       p1:textAppearance="?android:attr/textAppearanceLarge" 
 
       p1:id="@+id/txtAlwaysDisplay" 
 
       p1:layout_width="wrap_content" 
 
       p1:layout_height="wrap_content" 
 
       p1:layout_gravity="center_horizontal" /> 
 
     </LinearLayout> 
 
     <LinearLayout 
 
      p1:orientation="horizontal" 
 
      p1:minWidth="25px" 
 
      p1:minHeight="25px" 
 
      p1:layout_width="match_parent" 
 
      p1:layout_height="wrap_content" 
 
      p1:id="@+id/linToHideLineOne"> 
 
      <TextView 
 
       p1:text="To Hide Line One" 
 
       p1:textAppearance="?android:attr/textAppearanceLarge" 
 
       p1:id="@+id/txtHideLineOne" 
 
       p1:layout_width="wrap_content" 
 
       p1:layout_height="wrap_content" 
 
       p1:layout_gravity="center_horizontal" /> 
 
     </LinearLayout> 
 
     <LinearLayout 
 
      p1:orientation="horizontal" 
 
      p1:minWidth="25px" 
 
      p1:minHeight="25px" 
 
      p1:layout_width="match_parent" 
 
      p1:layout_height="wrap_content" 
 
      p1:id="@+id/linHideLineTwo"> 
 
      <TextView 
 
       p1:text="To Hide Line Two" 
 
       p1:textAppearance="?android:attr/textAppearanceLarge" 
 
       p1:id="@+id/txtHideLineTwo" 
 
       p1:layout_width="wrap_content" 
 
       p1:layout_height="match_parent" /> 
 
     </LinearLayout> 
 
    </LinearLayout> 
 
</FrameLayout>

Mi actividad y, a continuación, se parecía a lo siguiente:

using System; 
 

 
using Android.App; 
 
using Android.OS; 
 
using Android.Views; 
 
using Android.Widget; 
 
using Android.Animation; 
 
using Android.Views.Animations; 
 
using Android.Util; 
 

 
namespace MyNamespace 
 
{ 
 
    [Activity(Label = "testActivity")] 
 
    public class testActivity : Activity 
 
    { 
 
     public static string TAG = "M:testActivity"; 
 

 

 
     //by default we want the slider to be closed, which is why 
 
     // _sliderOpen has been set to true and we animate it into position when 
 
     //the window gets first focus 
 
     private bool _sliderOpen = true; 
 

 
     private ViewGroup _linMySlider; 
 
     private LinearLayout _linAlwaysDisplays; 
 

 
     private int _distanceToTravel; 
 

 
     protected override void OnCreate(Bundle savedInstanceState) 
 
     { 
 
      base.OnCreate(savedInstanceState); 
 

 
      SetContentView(Resource.Layout.testLayout); 
 

 
      _linMySlider = FindViewById<ViewGroup>(Resource.Id.linMySlider); 
 
      _linAlwaysDisplays = FindViewById<LinearLayout>(Resource.Id.linAlwaysDisplay); 
 

 
      TextView alwaysDisplayText = FindViewById<TextView>(Resource.Id.txtAlwaysDisplay); 
 
      alwaysDisplayText.Click += AlwaysDisplayText_Click; 
 
     } 
 

 
     private void AlwaysDisplayText_Click(object sender, EventArgs e) 
 
     { 
 
      DoAnimation(500); 
 
     } 
 

 
     public override void OnWindowFocusChanged(bool hasFocus) 
 
     { 
 
      base.OnWindowFocusChanged(hasFocus); 
 

 
      if (hasFocus) 
 
      { 
 
       if (_sliderOpen) 
 
       { 
 
        //we store this one time as it remains constant throught our sliding animations 
 
        _distanceToTravel = _linMySlider.Height - _linAlwaysDisplays.Height; 
 
        DoAnimation(1); 
 
       } 
 
      } 
 
     } 
 

 
     private void DoAnimation(long duration) 
 
     { 
 
      ObjectAnimator slideMe = null; 
 

 
      try 
 
      { 
 
       switch (_sliderOpen) 
 
       { 
 
        case true: 
 
         slideMe = ObjectAnimator.OfFloat(_linMySlider, "translationY", 0, _distanceToTravel); 
 
         _sliderOpen = false; 
 
         break; 
 
        case false: 
 
         slideMe = ObjectAnimator.OfFloat(_linMySlider, "translationY", _distanceToTravel, 0); 
 
         _sliderOpen = true; 
 
         break; 
 
       } 
 
       slideMe.SetInterpolator(new OvershootInterpolator()); 
 
       slideMe.SetDuration(duration); 
 
       slideMe.Start(); 
 
      } 
 
      catch (Exception e) 
 
      { 
 
       Log.Error(TAG, "DoAnimation: Exception - " + e.Message); 
 
      } 
 
     } 
 
    } 
 
}

El punto más importante a destacar es que la _distanceToTravel (en este caso la traducción en el eje Y) es relativa a la propiedad superior de la LinearLayout estamos animación. Supongamos que cada uno de los LinearLayouts que contienen el texto (SIEMPRE EN LA PANTALLA, Para Ocultar la Línea Uno, Para Ocultar la Línea Dos) tiene una altura de 20 (lo que hace que la altura total sea 60). El control deslizante, por ejemplo, tiene una propiedad Superior de 2100. Como se encuentra en la parte inferior, para ocultar las dos líneas, significa que tenemos que mover LinealLayout linMySlider 40 veces para ocultar las dos líneas, dejando solo la primera visible. Si piensas que LinearLayout es SIEMPRE 2100, entonces tiene sentido que en el deslizamiento hacia abajo le agreguemos 40 (bueno, no nosotros, el Animator lo hace por nosotros), evidente en la primera línea OfFloat, donde la posición inicial Y es 0 (es decir, 0 en relación con 2100, por lo que es igual a 2100) y su posición Y final es _distanceToTravel (que es 40, pero de nuevo relativa, por lo que equivale, de hecho, a 2140). En la dirección opuesta comenzamos con _distanceToTravel para Y (de nuevo 40, pero de hecho 2140) y terminamos en 0 (lo has adivinado 0 lejos de 2100 y, por lo tanto, 2100).

Espero que todo esto tenga sentido, me tomó un poco de tiempo para que el centavo caiga, pero funciona muy bien sin parpadeo y sin reinicio a la posición original (que siempre tuvo jajaja). Esperemos que lo mismo se aplique al código de Java como lo hace en este ejemplo de C#.

Cuestiones relacionadas