property fling animate android animation

fling - Android TranslateAnimation se reinicia después de la animación



image animation android (7)

Estoy creando algo así como un SlideDrawer, pero con la mayoría de las personalizaciones, básicamente funciona, pero la animación parpadea al final.

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 asombroso. Gracias


Desde la API 11, puede utilizar el ObjectAnimator , que realmente cambia las propiedades de la vista, es decir, en el caso de una traducción, la vista se mantendrá 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 .


Deshágase de setFillAfter y simplemente use View.GONE onAnimationEnd() . Vea here una muestra de vista personalizada que implementa un panel deslizante usando una TranslateAnimation .


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 definitivamente era el camino a seguir, y aquí está mi solución:

En primer lugar, un diseño simple con algo de texto en la parte superior y un 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, entonces, se veía como la 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 tener en cuenta es que _distanceToTravel (en este caso, se traduce en el eje Y) es relativo a la propiedad Top de LinearLayout que estamos animando. 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 caiga el centavo, 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 #.


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 por primera vez) que aún se puede obtener casi el mismo comportamiento que un oyente de animación al configurar un oyente por separado en la vista para ejecutar al final de su Vista en onAnimationStart() y en onAnimationEnd() .

Por ejemplo, si su código necesita desactivar un botón por 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, someView no sabe acerca de myButton , y me gustaría mantenerlo de esa manera. Solo puede crear un oyente en su clase de vista personalizada que se llame 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 regrese a 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 forma, mantendrá sus componentes separados de forma limpia mientras recibe un AnimationListener que funciona.



Usando la respuesta de Soham, aquí hay un ImageView específico para las animaciones de desvanecimiento:

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://.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í está 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); }


Here está el error real relacionado con este problema

Esto básicamente indica que el onAnimationEnd(...) no funciona bien cuando un AnimationListener está conectado a una animación.

La solución consiste en escuchar los eventos de animación en la vista a la que estaba aplicando la animación. Por ejemplo, si inicialmente estaba conectando el oyente de animación a la animación de esta manera.

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

y luego aplicar a la animación a un ImageView como este

mImageView.startAnimation(mAnimation);

Para evitar este problema, ahora debe crear un ImageView personalizado

public Class myImageView extends ImageView {

y luego anula el método onAnimationEnd de la clase View y proporciona toda la funcionalidad allí

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

Esta es la solución adecuada para este problema, proporcione la funcionalidad en el método View (Vista) -> onAnimationEnd(...) en comparación con el 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