web-dev-qa-db-fra.com

L'animation Android ne se répète pas

J'essaie de faire une animation simple qui se répète plusieurs fois (ou infiniment).
Il semble que Android:repeatCount ne fonctionne pas!
Voici ma ressource d'animation 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>

D'abord, l'image doit être redimensionnée de 1,0 à 1,2 en 500 ms.
Et puis redimensionner à 1,0 en 500 ms.
Voici comment je l’utilise:

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

Il fait un cycle et se termine ensuite. 
Il augmente, puis diminue et s’arrête.

Comment puis-je faire ce travail comme prévu?

77
Pavel Chernov

Mise à jour: De retour en septembre 2011, un ingénieur Android a résolu ce problème pour la plupart. Les attributs qui ont été ignorés dans XML fonctionnent maintenant, à l'exception de repeatCount et fillEnabled qui sont toujours ignorés (exprès pour une raison quelconque). Cela signifie qu'il n'est toujours pas facile de répéter un AnimationSet malheureusement.

Pour plus de détails, veuillez consulter l'aperçu dans la mise à jour de la documentation (explique quels attributs sont ignorés, lesquels fonctionnent et ceux qui sont transmis aux enfants). Et pour mieux comprendre ce que fillAfter, fillBefore et fillEnabled font réellement, consultez le blog de l'ingénieur (Chet Haase) à ce sujet (ici } _.


Réponse originale

Pour développer les réponses de Pavel et d’autres: il est vrai que la balise <set> est ridiculement boguée. Il ne peut pas traiter correctement repeatCount et un certain nombre d'autres attributs.

J'ai passé quelques heures à comprendre ce qu'il peut et ne peut pas gérer et j'ai soumis un rapport de bogue/un problème ici: Problème 17662

En résumé (cela concerne AnimationSets):

setRepeatCount ()/Android: repeatCount

Cet attribut (ainsi que repeatMode) ne fonctionne pas en code ou en XML. Cela rend difficile la répétition d'un ensemble d'animations.

setDuration ()/Android: durée 

Définir ceci sur un AnimationSet dans le code WORKS (remplace toutes les durées des animations enfants), mais pas lorsqu'il est inclus dans la balise en XML

setFillAfter ()/Android: fillAfter 

Cela fonctionne à la fois en code et en XML pour la balise. Étrangement, je l'ai obtenu pour qu'il fonctionne également sans qu'il soit nécessaire de définir fillEnabled sur true.

setFillBefore ()/Android: remplirAvant 

Semble n'avoir aucun effet/ignoré dans le code et XML

setFillEnabled ()/Android: fillEnabled

Semble n'avoir aucun effet/ignoré à la fois en code et en XML. Je peux toujours faire en sorte que fillAfter fonctionne même sans inclure fillEnabled ni définir la valeur de fillEnabled à false.

setStartOffset ()/Android: startOffset

Cela ne fonctionne que dans le code et pas XML.

62
Tony Chan

J'ai trouvé que <set> tag a une implémentation erronée dans la classe AnimationSet .
Il ne peut pas traiter correctement avec repeatCount .
Ce que nous pouvons faire est de définir repeatCount directement dans <scale> tag.

Cette ressource XML fonctionne 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"
/>

Malheureusement, ceci est limité à une seule animation à la fois.
Nous ne pouvons pas définir une séquence d'animations de cette façon ...

41
Pavel Chernov

Vous devriez inclure l'attribut

Android:repeatCount="infinite"

Mais dans votre animation "scale" pas dans "set"

38
Macumbaomuerte

Pour obtenir une animation répétée, j'ai utilisé l'écouteur d'animation et j'ai appelé l'animation à nouveau à la fin. Cela fait un réticule de caméra se concentrant comme une animation avec des crochets.

Voici la mise en page d'animation 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>

Voici le code 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);
}
31
Danuofr

Je faisais également face au même problème .. J'ai inclus Android: repeatCount = "infinite" dans le fichier XMl..Maintenant, il fonctionne 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"/>

10
abhilash

vous pouvez essayer ce code . Dans votre code, ajoutez simplement:

firstAnimation.setRepeatCount(5);

Cela va répéter l'animation pour un temps précis

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

Cela répétera l'animation indéfiniment.

9
Informatics

Ajoutez la classe suivante à votre projet:

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

Pour une boucle infinie de votre vue, procédez comme suit:

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

Si vous souhaitez répéter l'animation seulement N fois, procédez comme suit:

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

N représente le nombre de répétitions.

2
domi

J'ai essayé d'utiliser le code de Daniel pour afficher l'animation le nombre exact de fois et j'ai eu un problème: l'animation a été affichée approximativement n/2 fois, quand n fois attendue. 

J'ai donc modifié le code 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);
            }
        }  
    } 
}
//... 

En utilisant la variante ci-dessus, l'animation est montrée exactement fois REPEAT_COUNT, car la méthode View.post () donne la possibilité de démarrer une nouvelle animation après avoir terminé toutes les actions, liée à l'animation précédente.

2
Denis

vous devez ajouter juste une ligne dans votre code XML que j'ai suggéré ci-dessous. 

<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>
2
Mitesh Sawant

Avec Android SDK version 4.0.3:

Dans les éléments d'animation donnés:

Android: repeatCount = "- 1"

en fait une animation infinie.

2
Kurt Junghanns

J'ai résolu ce problème en utilisant reverse avant dans mon projet. 

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

1
Huseyin

je voulais que ça aille ... J'essayais de voir une vue tourner en cercle de façon continue.

j'utilisais auparavant rotation.setRepeatMode (-1) mais cela ne fonctionnait pas. changé pour setrepeatcount et cela fonctionne. Ceci est sur Jelly Bean 4.2.2

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

Je fais la plupart de mes choses par programme et je peux être en retard ou inefficace sur celui-ci, mais cela, mais j'ai complété l'objectif de répétition animationset (j'ai même deux alternances d'animations). Tout ce code ne fait que fondre dans une image, faire une pause, puis disparaître, puis fondre dans une autre image, faire une pause, disparaître et ramener la première (rincer et répéter). J'ai d'abord défini mes images vues:

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

Ensuite, j'ai créé deux minuteries, des minuteries et des gestionnaires, pour commencer et arrêter chaque animation:

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

Enfin, plutôt que de créer des ensembles d'animations en ajoutant des animations, je me contente de créer des animations pour les auditeurs afin de déterminer quand commencer chaque animation:

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

        }
    });
}    

Le clearanimation et invalider où juste tentatives précédentes et obtenir cette chose pour fonctionner correctement. Je ne sais pas si elles sont nécessaires ou non.

J'espère que ça aide quelqu'un.


Ryan

1
testingtester

je viens de rencontrer ce problème alors que je travaillais sur une application rétro-compatible. tellement frustrant! J'ai fini par coder une classe de solution de contournement Nice qui peut être appelée depuis onCreate et lancera toute ressource d'animation dans une boucle indéfinie. 

la classe, AnimationLooper, est disponible ici: https://Gist.github.com/2018678

0
newbyca

J'ai résolu ce problème en utilisant du fil.

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
nikeru8

Aucune des solutions ci-dessus n'a fonctionné dans mon cas. La solution de Danuofr a fonctionné pour l'ensemble d'animation, mais lorsque je faisais des tests unitaires, mes tests étaient bloqués dans cette boucle infinie. Enfin, en fonction de mon cas, je devais répéter cette animation un nombre de fois spécifique. J'ai donc ajouté manuellement des copies de mon animation dans anim_rot.xml de manière cascadée en ajoutant la valeur de décalage . Je sais que c'est mauvais et que cela ne fonctionnera pas pour beaucoup, mais c'était la seule solution de contournement pour mon cas. 

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>

Je l'ai fait pour répéter l'animation 3 fois. Vous pouvez ajouter plus de copies pour répéter des heures spécifiques en ajoutant des valeurs de décalage.

0
Sachin Aggarwal

Little Tweak to @Danufr répond pour sauver les ressources du chargement.

    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
Mr Coder

Après avoir recherché des réponses sur Internet, j'ai trouvé une solution qui me convient parfaitement. (Et oui, repeatCount et repeatMode sont extrêmement bogués lorsqu'ils sont utilisés avec 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 activité: (Résolvez-le en introduisant un léger retard après la fin de l’animation).

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

Il y a beaucoup de cours sur lesquels vous aimeriez faire des recherches, mais j'utilise actuellement objectAnimator, qui est très flexible. Je ne recommanderais pas d'utiliser Animation ou AnimationUtils:

  • Animation
  • AnimationUtils
  • Animateur
  • AnimateurInflater
  • AnimateurListener
  • AnimatorListenerAdapter
0
morph85

Il est nécessaire d’écouter la fin de la première animation, puis de la relancer dans le rappel onStopAnimation, essayez-le link

0
Ravi K. Sharma

Essayez d’ajouter le code à un thread en boucle ou à une instruction while/for

0
carefacerz

Je suis confronté au même problème, mais je ne souhaite effectuer aucune opération de chronométrage en Java, car le thread d'interface utilisateur peut parfois être très occupé .. .. L'indicateur INFINITE ne fonctionne pas pour la balise set. J'ai donc résolu le problème avec un petit morceau de code:

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

avec le XML suivant:

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

Où mIcon est un ImageView de ma mise en page.

0
Jordi

ça marche bien 

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

J'ai résolu ce problème. Ceci est ma version du correctif:

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
xander_blr