La animación de Android no se repite


85

Estoy tratando de hacer una animación simple que se repita varias veces (o infinitamente).
¡Parece que android:repeatCountno funciona!
Aquí está mi recurso de animación de /res/anim/first_animation.xml:

<?xml version="1.0" encoding="utf-8"?>
<set
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="false"
    android:repeatCount="infinite"
    >
    <scale
        android:interpolator="@android:anim/decelerate_interpolator"
        android:duration="500"
        android:fromXScale="1.0"
        android:fromYScale="1.0"
        android:toXScale="1.2"
        android:toYScale="1.2"
        android:pivotX="50%"
        android:pivotY="50%"
        android:fillAfter="false" />
    <scale
        android:interpolator="@android:anim/accelerate_interpolator"
        android:startOffset="500"
        android:duration="500"
        android:fromXScale="1.2"
        android:fromYScale="1.2"
        android:toXScale="1.0"
        android:toYScale="1.0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:fillAfter="false" />
</set>

Primero debe escalar la imagen de 1.0 a 1.2 en 500 ms.
Y luego vuelva a escalarlo a 1.0 en 500 ms.
Así es como lo estoy usando:

Animation firstAnimation = AnimationUtils.loadAnimation(this, R.anim.first_animation);
imgView.startAnimation(firstAnimation);

Hace un ciclo y luego termina.
Se amplía, luego se reduce y luego se detiene.

¿Cómo puedo hacer que esto funcione según lo previsto?


¿Qué es imgView aquí en su código Java?
clifgray

Respuestas:


63

Actualización: en septiembre de 2011, un ingeniero de Android solucionó este problema en su mayor parte. Los atributos que se ignoraron en XML ahora funcionan, con la excepción de repeatCounty fillEnabledque todavía se ignoran (a propósito por alguna razón). Esto significa que, AnimationSetlamentablemente , todavía no es fácil repetir .

Para obtener más detalles, consulte la descripción general en los documentos actualizados (explica qué atributos se ignoran, cuáles funcionan y cuáles se pasan a los niños). Y para una comprensión más profunda de lo que fillAfter, fillBeforey fillEnabledde hecho hacer, ver (Chet Haase) entrada en el blog del ingeniero de ello aquí .


Respuesta original

Para ampliar las respuestas de Pavel y otros: es cierto que la <set>etiqueta tiene errores ridículos. No se puede tratar correctamente con repeatCountuna serie de otros atributos.

Pasé unas horas averiguando qué puede y qué no puede manejar y he enviado un informe de error / problema aquí: Problema 17662

En resumen (esto se refiere a AnimationSets):

setRepeatCount () / android: repeatCount

Este atributo (al igual que repeatMode) no funciona en código o XML. Esto dificulta la repetición de un conjunto completo de animaciones.

setDuration () / android: duración

Establecer esto en un AnimationSet en el código WORKS (anula todas las duraciones de las animaciones secundarias), pero no cuando se incluye en la etiqueta en XML

setFillAfter () / android: fillAfter

Esto funciona tanto en código como en XML para la etiqueta. Curiosamente, he conseguido que también funcione sin la necesidad de establecer fillEnabled en true.

setFillBefore () / android: fillBefore

Parece no tener efecto / ignorado tanto en código como en XML

setFillEnabled () / android: fillEnabled

Parece no tener efecto / ignorado tanto en código como en XML. Todavía puedo hacer que fillAfter funcione incluso sin incluir fillEnabled o establecer fillEnabled en falso.

setStartOffset () / android: startOffset

Esto solo funciona en código y no en XML.


48

Descubrí que la etiqueta <set> tiene una implementación defectuosa en la clase AnimationSet .
No puede lidiar correctamente con repeatCount .
Lo que podemos hacer es establecer repeatCount directamente en la etiqueta <scale> .

Este recurso XML funciona bien:

<?xml version="1.0" encoding="utf-8"?>
<scale
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:duration="200"
    android:fromXScale="1.0"
    android:fromYScale="1.0"
    android:toXScale="1.05"
    android:toYScale="1.05"
    android:pivotX="50%"
    android:pivotY="50%"
    android:repeatMode="reverse"
    android:fillAfter="false"
    android:repeatCount="24"
/>

Desafortunadamente, esto se limita a una sola animación a la vez.
No podemos definir una secuencia de animaciones de esta manera ...


Estoy ejecutando 2 animaciones en un conjunto y no me dan ningún problema. por favor dígame de qué problema está hablando. cual error actualmente trabajando en 1.6 sdk
AZ_

Declarando repeatCount en las obras XML, pero no en código
onmyway133

39

Debes incluir el atributo

android:repeatCount="infinite"

Pero en la animación de "escala" no está en "conjunto"


1
pero ¿estas animaciones esperarán a que se complete la anterior? gracias
filthy_wizard

Gracias, esto funcionó! Configurarlo programáticamente no lo hizo por alguna razón.
cherry-wave

¡Gracias! Esto funcionó. Pero es continuo. ¿Es posible hacer que esto suceda cada 5 segundos?
d34th4ck3r

32

Para obtener una animación repetida, utilicé el oyente de animación y volví a llamar a la animación cuando terminó. Esto hace que la retícula de la cámara se enfoque como una animación con corchetes.

Aquí está el diseño de animación xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
    android:fromXScale="1.0"
    android:toXScale=".7"
    android:fromYScale="1.0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toYScale=".7"
    android:duration="1000"/>
<scale 
    android:duration="1000"
    android:fromXScale=".7"
    android:toXScale="1.0"
    android:fromYScale=".7"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toYScale="1.0"
    android:startOffset="1000"/>

</set>

Aquí está el código java

 public void startAnimation() {

            View brackets = findViewById(R.id.brackets);
            brackets.setVisibility(View.VISIBLE);

            Animation anim = AnimationUtils.loadAnimation(BuzzFinderActivity.this, R.anim.crosshair_focusing);
            anim.setAnimationListener(new AnimationListener() {

                @Override
                public void onAnimationEnd(Animation arg0) {
                    Animation anim = AnimationUtils.loadAnimation(BuzzFinderActivity.this, R.anim.crosshair_focusing);
                    anim.setAnimationListener(this);
                    brackets.startAnimation(anim);

                }

                @Override
                public void onAnimationRepeat(Animation arg0) {
                    // TODO Auto-generated method stub

                }

                @Override
                public void onAnimationStart(Animation arg0) {
                    // TODO Auto-generated method stub

                }

            });


            brackets.startAnimation(anim);
}

2
Ya debería ser la respuesta correcta. Trabajando en todos los dispositivos y niveles de sistema operativo
Smeet

también me ayudó, pero eliminé estas dos líneas del método End Animation anim = AnimationUtils.loadAnimation (BuzzFinderActivity.this, R.anim.crosshair_focusing); anim.setAnimationListener (esto);
aida

10

También me enfrentaba al mismo problema ... incluí android: repeatCount = "infinite" en el archivo XMl ... ahora funciona bien ...

  <translate 
           android:fromXDelta="0"
           android:toXDelta="80"
           android:duration="1000"
           android:repeatCount="infinite"   
           android:repeatMode="reverse" 
           android:pivotX="50%"
           android:pivotY="50%"                             
           android:fillAfter="true"/>


9

puedes probar este código. En su código simplemente agregue,

firstAnimation.setRepeatCount(5);

Esto repetirá la animación por un tiempo definido.

firstAnimation.setRepeatCount(Animation.INFINITE);
firstAnimation.setRepeatMode(Animation.INFINITE);

Esto repetirá la animación indefinidamente.


4
repeatModedebe ser RESTARToREVERSE
xinthink

eso es exactamente lo que quiero, configurado para configurar dinámicamente al infinito.
Varun Chaudhary


4

Intenté usar el código de Daniel para mostrar la animación el número exacto de veces y tuve un problema: la animación se mostraba aproximadamente n / 2 veces, cuando se esperaba n veces.

Así que modifiqué el código de Daniel:

//...
@Override
public void onAnimationEnd(Animation arg0) {
    mCurrentCount++;
    if (mCurrentCount < REPEAT_COUNT) {  
        Animation anim = AnimationUtils.loadAnimation(BuzzFinderActivity.this, R.anim.crosshair_focusing);
        anim.setAnimationListener(this);
        brackets.post(new Runnable() {
            @Override
            public void run() {
                brackets.startAnimation(anim);
            }
        }  
    } 
}
//... 

Usando la variante, que se muestra arriba, la animación se muestra exactamente REPEAT_COUNT veces, porque el método View.post () brinda la capacidad de comenzar una nueva animación después de terminar todas las acciones, relacionadas con la animación anterior.


3

debe agregar solo una línea en su código xml que sugerí a continuación.

<scale
    android:duration="500"
    android:fromXScale="1.0"
    android:fromYScale="1.0"
    android:toXScale="1.2"
    android:toYScale="1.2"
    android:pivotX="50%"
    android:pivotY="50%"
    android:repeatCount="infinite" // just add this one line 
    android:fillAfter="false"
    />
</set>

3

Resolví este problema usando android:repeatMode="reverse"antes en mi proyecto.

<scale
    android:interpolator="@android:anim/decelerate_interpolator"
    android:duration="500"
    android:fromXScale="1.0"
    android:fromYScale="1.0"
    android:toXScale="1.2"
    android:toYScale="1.2"
    android:pivotX="50%"
    android:pivotY="50%"
    android:repeatMode="reverse"
    android:repeatCount="infinite" />

2

Con la versión 4.0.3 de Android SDK:

En los elementos de animación dados:

android: repeatCount = "- 1"

lo convierte en una animación infinita.


¡Gracias! Funciona bien en 4.2 sin ninguna solución alternativa
ruX

2

Agregue la siguiente clase a su proyecto:

import android.view.View;
import android.view.animation.Animation;

public class AnimationRepeater implements Animation.AnimationListener
{
    private View view;
    private Animation animation;
    private int count;

    public AnimationRepeater(View view, Animation animation)
    {
        this.view = view;
        this.animation = animation;
        this.count = -1;
    }

    public AnimationRepeater(View view, Animation animation, int count)
    {
        this.view = view;
        this.animation = animation;
        this.count = count;
    }

    public void start()
    {
        this.view.startAnimation(this.animation);
        this.animation.setAnimationListener(this);
    }

    @Override
    public void onAnimationStart(Animation animation) { }

    @Override
    public void onAnimationEnd(Animation animation)
    {
        if (this.count == -1)
            this.view.startAnimation(animation);
        else
        {
            if (count - 1 >= 0)
            {
                this.animation.start();
                count --;
            }
        }
    }

    @Override
    public void onAnimationRepeat(Animation animation) { }
}

Para un bucle infinito de su vista, haga lo siguiente:

Animation a = AnimationUtils(Context, R.anim.animation);
new AnimationRepeater(View, a).start();

Si desea repetir la animación solo N veces, haga lo siguiente:

Animation a = AnimationUtils(Context, R.anim.animation);
new AnimationRepeater(View, a, int N).start();

N significa número de repeticiones.


1

Hago la mayoría de mis cosas de manera programática y puedo llegar tarde o ser ineficiente en esto, pero esto, pero completé el objetivo de repetir el conjunto de animación (incluso tengo 2 conjuntos de animación alternos). Todo lo que hace este código es simplemente desvanecerse en una imagen, pausar, luego desvanecerse, desvanecerse en otra imagen, pausar, desvanecerse y recuperar la primera (enjuagar y repetir). Primero definí mis Imageviews:

    final ImageView purple = (ImageView)findViewById(R.id.purp);
    final ImageView yellow = (ImageView)findViewById(R.id.yell);
    purple.setVisibility(View.INVISIBLE);
    yellow.setVisibility(View.INVISIBLE);

Luego hice dos temporizadores, temporizadores de tareas y controladores para lidiar con cuándo iniciar y detener cada animación:

    Timer p = new Timer();
    TimerTask pu = new TimerTask() {
        public void run() {
                handler1.post(new Runnable() {
                        public void run() 
                        {
                           fadein(purple);
                        }
               });
        }};
        p.schedule(pu, 6000, 12000);

    final Handler handler2 = new Handler();

    Timer y = new Timer();
    TimerTask ye = new TimerTask() {
        public void run() {
                handler2.post(new Runnable() {
                        public void run() 
                        {
                           fadein(yellow);
                        }
               });
        }};

        y.schedule(ye, 0, 12000);

Finalmente, en lugar de crear conjuntos de animación agregando animaciones, solo los oyentes de animaciones para determinar cuándo comenzar cada animación:

public void fadein (final ImageView image)
{
    Animation anim = new AlphaAnimation(0, 1);

    anim.setDuration(2000);

    image.startAnimation(anim);
    anim.setAnimationListener(new AnimationListener() {
        public void onAnimationEnd(Animation animation) 
        {
            image.clearAnimation();
            image.invalidate();
            pause(image);

        }

        @Override
        public void onAnimationRepeat(Animation animation) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onAnimationStart(Animation animation) {
            // TODO Auto-generated method stub

        }
    });
}    
public void pause (final ImageView image)
{
    Animation anim = new AlphaAnimation(1, 1);

    anim.setDuration(2000);

    image.startAnimation(anim);
    anim.setAnimationListener(new AnimationListener() {
        public void onAnimationEnd(Animation animation) 
        {
            image.clearAnimation();
            image.invalidate();
            fadeout(image);

        }

        @Override
        public void onAnimationRepeat(Animation animation) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onAnimationStart(Animation animation) {
            // TODO Auto-generated method stub

        }
    });
}     
public void fadeout (final ImageView image)
{
    Animation anim = new AlphaAnimation(1,0);

    anim.setDuration(2000);

    image.startAnimation(anim);
    anim.setAnimationListener(new AnimationListener() {
        public void onAnimationEnd(Animation animation) 
        {
            image.clearAnimation();
            image.invalidate();
        }

        @Override
        public void onAnimationRepeat(Animation animation) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onAnimationStart(Animation animation) {
            // TODO Auto-generated method stub

        }
    });
}    

La limpieza y la invalidación fueron solo intentos anteriores y hacer que esto funcione correctamente. No sé si son obligatorios o no.

Espero que esto ayude a alguien.


Ryan


1

Tengo esto para ir ... Estaba tratando de obtener una vista para rotar en un círculo continuamente.

anterior, estaba usando Rotation.setRepeatMode (-1) pero eso no funcionó. cambió a setrepeatcount y funciona. Esto está en Jelly Bean 4.2.2

 ObjectAnimator rotation = ObjectAnimator.ofFloat(myview,
                          "rotation", 360).setDuration(2000);
                rotation.setRepeatMode(-1);
          rotation.setRepeatCount(Animation.INFINITE); 
 rotation.start();

0

Me enfrenté al mismo problema, pero no quería hacer nada de sincronización en Java debido al punto que el hilo de la interfaz de usuario puede estar muy ocupado a veces. La bandera INFINITE no funciona para la etiqueta de conjunto. Así que resolví el problema con un pequeño fragmento de código:

mAnimation = (AnimationSet) AnimationUtils.loadAnimation(myContext, R.anim.blink);
mIcon.startAnimation(mAnimation);
mAnimation.setAnimationListener(new AnimationListener() {
    public void onAnimationStart(Animation animation) {}
    public void onAnimationRepeat(Animation animation) {}
    public void onAnimationEnd(Animation animation) {
        mIcon.startAnimation(mAnimation);
    }
});

con el siguiente XML:

<alpha
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:fromAlpha="0.0"
    android:toAlpha="1.0" />

<alpha
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:fromAlpha="0.9"
    android:startOffset="1000"
    android:toAlpha="0.0" />

Donde mIcon es un ImageView de mi diseño.


0

He resuelto este problema. Esta es mi versión de la solución:

public class HelloAndroidActivity extends Activity {
private static String TAG = "animTest";
private Animation scaleAnimation;
private int currentCover = 0;
private List<ImageView> imageViews = new ArrayList<ImageView>(3);
private Button btn;
private ImageView img;

/**
 * Called when the activity is first created.
 * @param savedInstanceState If the activity is being re-initialized after 
 * previously being shut down then this Bundle contains the data it most 
 * recently supplied in onSaveInstanceState(Bundle). <b>Note: Otherwise it is null.</b>
 */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.i(TAG, "onCreate");
    setContentView(R.layout.test);

    img = (ImageView)findViewById(R.id.testpict);
    imageViews.add(img);
    img = (ImageView)findViewById(R.id.testpictTwo);
    imageViews.add(img);
    img = (ImageView)findViewById(R.id.testpict3);
    imageViews.add(img);

    scaleAnimation = AnimationUtils.loadAnimation(this, R.anim.photo_scale);
    scaleAnimation.setAnimationListener(new CyclicAnimationListener());

    btn = (Button)findViewById(R.id.startBtn);
    btn.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            imageViews.get(0).startAnimation(scaleAnimation);
        }
    });



}

private class CyclicAnimationListener implements AnimationListener{

    @Override
    public void onAnimationEnd(Animation animation) {
        currentCover += 1;
        if(currentCover >= imageViews.size()){
            currentCover = 0;
        }
        img = imageViews.get(currentCover);
        scaleAnimation = AnimationUtils.loadAnimation(HelloAndroidActivity.this, R.anim.photo_scale);
        scaleAnimation.setAnimationListener(new CyclicAnimationListener());
        img.startAnimation(scaleAnimation);
    }

    @Override
    public void onAnimationRepeat(Animation animation) {
        Log.d("Animation", "Repeat");
    }

    @Override
    public void onAnimationStart(Animation animation) {

    }

}

}

0

Me encontré con este problema mientras trabajaba en una aplicación compatible con versiones anteriores. ¡muy frustrante! Terminé codificando una buena clase de solución alternativa que se puede llamar desde onCreate y que iniciará cualquier recurso de animación en un bucle indefinido.

la clase, AnimationLooper, está disponible aquí: https://gist.github.com/2018678


0

Después de investigar las respuestas de Internet, encontré una solución que funciona perfectamente para mí. (Y sí, repeatCount y repeatMode tienen muchos errores cuando se usan junto con animationSet).

anim_rotate_fade.xml:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:ordering="together" >

    <objectAnimator
        android:duration="3000"
        android:propertyName="rotation"
        android:repeatCount="1"
        android:valueTo="360"
        android:valueType="floatType" />

    <objectAnimator
        android:duration="3000"
        android:propertyName="alpha"
        android:repeatCount="1"
        android:repeatMode="reverse"
        android:valueFrom="0.0"
        android:valueTo="0.3"
        android:valueType="floatType" />

    <objectAnimator
        android:duration="3000"
        android:propertyName="y"
        android:repeatCount="1"
        android:repeatMode="reverse"
        android:valueFrom="380"
        android:valueTo="430"
        android:valueType="floatType" />

</set>

En actividad: (Resuélvalo introduciendo un ligero retraso una vez finalizada la animación).

ImageView starlightImageView = new ImageView(this);
starlightImageView.setImageResource(R.drawable.starlight);
final AnimatorSet animate = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.anim.anim_rotate_fade);
AnimatorListenerAdapter animatorListener = new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        super.onAnimationEnd(animation);
        new Handler().postDelayed(new Runnable() {
            @Override public void run() {
                animate.start();
            }
        }, 1000);
    }
};
animate.setTarget(starlightImageView);
animate.addListener(animatorListener);

Hay muchas clases sobre las que te gustaría investigar, pero actualmente estoy usando objectAnimator, que es muy flexible. No recomendaría usar Animation o AnimationUtils:

  • Animación
  • AnimationUtils
  • Animador
  • AnimadorInflater
  • AnimatorListener
  • AnimatorListenerAdapter

0

Es necesario escuchar la finalización de la primera animación y luego reiniciar la animación en la devolución de llamada de onStopAnimation, pruebe este enlace


0

Un pequeño ajuste a la respuesta de @Danufr para evitar que los recursos se carguen nuevamente.

    operator = (ImageView) findViewById(R.id.operator_loading);
  final  Animation ani = AnimationUtils.loadAnimation(getApplicationContext(),R.anim.finding_operator);


    ani.setAnimationListener(new Animation.AnimationListener() {
        @Override
        public void onAnimationStart(Animation animation) {

        }

        @Override
        public void onAnimationEnd(Animation animation) {

            operator.startAnimation(ani);

        }

        @Override
        public void onAnimationRepeat(Animation animation) {

        }
    });

    operator.setAnimation(ani);

0

Resolví este problema usando hilo.

Button btn = (Button) findViewById(R.id.buttonpush);
    final TextView textview = (TextView) findViewById(R.id.hello);
    btn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            textview.setText("...................");
            final Animation animationtest = AnimationUtils.loadAnimation(MainActivity.this, android.R.anim.slide_in_left);
            animationtest.setDuration(1000);

            final Handler handler = new Handler();
            Runnable runnable = new Runnable() {
                public void run() {
                    handler.postDelayed(this, 1500);
                    textview.startAnimation(animationtest);
                }
            };
            handler.postDelayed(runnable, 500); // start
            handler.removeCallbacks(runnable); //STOP Timer

        }
    });

0

está funcionando bien

 GifDrawable gifDrawable = (GifDrawable) gifImageView.getDrawable();
    gifDrawable.setLoopCount(0);

0

Ninguna de las soluciones anteriores funcionó en mi caso. La solución de Danuofr funcionó para el conjunto de animación, pero cuando estaba haciendo pruebas unitarias, mis pruebas solían atascarse en este ciclo infinito. Finalmente, específicamente para mi caso, necesitaba repetir esta animación un número específico de veces. Entonces, agregué manualmente copias de mi animación en anim_rot.xml en cascada agregando el valor de compensación . Sé que es malo y no funcionará para muchos, pero fue la única solución para mi caso.

anim_rot.xml

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <rotate
        android:duration="2000"
        android:fromDegrees="20"
        android:pivotX="29%"
        android:pivotY="50%"
        android:toDegrees="-20" />
    <rotate
        android:duration="2000"
        android:fromDegrees="-20"
        android:pivotX="29%"
        android:pivotY="53%"
        android:startOffset="2000"
        android:toDegrees="20" />
    <rotate
        android:startOffset="4000"
        android:duration="2000"
        android:fromDegrees="20"
        android:pivotX="29%"
        android:pivotY="56%"
        android:toDegrees="-20" />
    <rotate
        android:duration="2000"
        android:fromDegrees="-20"
        android:pivotX="29%"
        android:pivotY="59%"
        android:startOffset="6000"
        android:toDegrees="20" />
    <rotate
        android:startOffset="8000"
        android:duration="2000"
        android:fromDegrees="20"
        android:pivotX="29%"
        android:pivotY="62%"
        android:toDegrees="-20" />
    <rotate
        android:duration="2000"
        android:fromDegrees="-20"
        android:pivotX="29%"
        android:pivotY="65%"
        android:startOffset="10000"
        android:toDegrees="20" />
</set>

Hice esto para repetir la animación 3 veces. Puede agregar más copias para repetirlo veces específicas agregando valores de compensación.


-1

Intente agregar el código a un hilo de bucle o una declaración while / for


Bueno, la única solución que he encontrado es evitar usar set tag.
Pavel Chernov
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.