web-dev-qa-db-fra.com

onAnimationEnd n'est pas appelé, onAnimationStart fonctionne bien

J'ai un ScrollView dans le PopupWindow. J'anime le contenu de ScrollView à l'aide de TranslateAnimation.

Lorsque l'animation démarre, le programme d'écoute onAnimationStart est appelé, mais onAnimationEnd n'est pas appelé. Des idées ?

Disposition :

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout
   xmlns:Android="http://schemas.Android.com/apk/res/Android"
   Android:background="@drawable/popup_window_bg"
   Android:layout_width="match_parent"
   Android:layout_height="wrap_content">
  <View
     Android:layout_width="@dimen/toolbar_padding_left"
     Android:layout_height="@dimen/toolbar_height"/>
  <ScrollView
     xmlns:Android="http://schemas.Android.com/apk/res/Android"
     Android:id="@+web/toolbar"
     Android:layout_width="wrap_content"
     Android:layout_height="wrap_content"
     Android:scrollbars="none"
     Android:visibility="invisible">
    <LinearLayout
       Android:layout_width="match_parent"
       Android:layout_height="wrap_content"
       Android:orientation="vertical">

       ...

    </LinearLayout>
  </ScrollView>
</LinearLayout>

Code d'animation:

mToolbar = mPopupContents.findViewById( R.web.toolbar );
TranslateAnimation anim =
    new TranslateAnimation(0, 0, -60, 0);
anim.setDuration(1000);
anim.setAnimationListener(new Animation.AnimationListener() {
        public void onAnimationStart(Animation a) {
            Log.d(LOGTAG, "---- animation start listener called"  );
        }
        public void onAnimationRepeat(Animation a) {}
        public void onAnimationEnd(Animation a) {
            Log.d(LOGTAG, "---- animation end listener called"  );
        }
    });
mToolbar.startAnimation(anim);

Update : J'ai vérifié que le onAnimationEnd est appelé, mais après un certain délai (si vous ne démarrez pas la nouvelle animation pendant ce temps).

42
Karan

AnimationEnd n'est pas fiable. Si vous ne souhaitez pas réécrire votre code avec des vues personnalisées remplaçant OnAnimationEnd, utilisez postDelayed.

Voici un exemple de code:

final FadeUpAnimation anim = new FadeUpAnimation(v);
anim.setInterpolator(new AccelerateInterpolator());
anim.setDuration(1000);
anim.setFillAfter(true);
new Handler().postDelayed(new Runnable() {
    public void run() {
        v.clearAnimation();
        //Extra work goes here
    }
}, anim.getDuration());
v.startAnimation(anim);

Bien que cela puisse paraître moche, je peux vous garantir que c'est très fiable. Je l'utilise pour ListViews qui insère de nouvelles lignes tout en supprimant une animation avec d'autres lignes. Le test de stress d'un auditeur avec AnimationEnd s'est avéré peu fiable. Parfois, AnimationEnd n'a jamais été déclenché. Vous voudrez peut-être réappliquer toute transformation dans la fonction postDelayed au cas où l'animation ne serait pas complètement terminée, mais cela dépend vraiment du type d'animation que vous utilisez.

56
ShortFuse

Après que je ne me rappelle plus comment les publications pouvaient être lues et les jours consacrés à la recherche d'une solution à ce problème, j'ai découvert que si l'objet à déplacer ne se trouvait pas dans la région Screen (par exemple, il est positionné en dehors des coordonnées de l'écran), le rappel OnAnimationEnd n'est pas appelé. Probablement l'animation échoue juste après le début (la méthode start est appelée, j'ai codé un écouteur) mais rien n'est écrit dans logcat. Ce n'est peut-être pas tout à fait votre cas, mais cela a finalement résolu mon problème et j'espère que cela pourra vous aider aussi.

9
Lisitso

Assurez-vous que vous UTILISEZview.startAnimation(Animation)AND NOTview.setAnimation(Animation). Cette simple confusion peut être un problème.

7
ralphgabb

De plus, lorsque vous utilisez des animations, n'oubliez pas la fonction setFillAfter() à true.

http://developer.Android.com/reference/Android/view/animation/Animation.html#setFillAfter(boolean)

Si fillAfter est à true, la transformation effectuée par cette animation persistera à la fin. La valeur par défaut est false si elle n'est pas définie. Notez que cela s'applique lorsque vous utilisez un AnimationSet pour enchaîner des animations. La transformation n'est pas appliquée avant le démarrage de AnimationSet.

anim.setFillAfter(true);
mToolbar.startAnimation(anim);
3
Entreco

Pour ceux qui tombent sur cette question: envisagez d’utiliser plutôt le système Property Animation http://developer.Android.com/guide/topics/graphics/prop-animation.html

J'ai eu plusieurs problèmes avec l'ancienne méthode d'animation d'un fondu enchaîné sur une vue (via AlphaAnimation). OnAnimationEnd n'était pas appelé, etc. Avec la Property Animation, tous ces problèmes ont été résolus.

Si vous souhaitez prendre en charge les périphériques API <11, utilisez https://github.com/JakeWharton/NineOldAndroids de Jake Wharton est la voie à suivre

2
MrMaffen

J'ai essayé votre code, cela fonctionne bien au début OnAnimation et inAmimationEnd également, après la durée, après l'animation terminée, onAnimationEnd est appelée, votre code fonctionne donc bien. 

TranslateAnimation anim =new TranslateAnimation(0, 0, -60, 0);
        anim.setDuration(1000);     
        anim.setAnimationListener(new Animation.AnimationListener() {
                public void onAnimationStart(Animation a) {
                    Log.w("Start", "---- animation start listener called"  );
                }
                public void onAnimationRepeat(Animation a) {}
                public void onAnimationEnd(Animation a) {
                    Log.d(" end  ","---- animation end listener called"  );
                }
            });
            mIv.setAnimation(anim);
            mIv.startAnimation(anim);
0
sravan

Essayez d'utiliser overridePendingAnimation (int, int) . I.e . OverridePendingAnimation (0,0)

Elle remplacera votre animation par défaut et vous pourrez ensuite définir votre propre animation en appelant la méthode startAnimation à l'aide de l'objet View.

Voici mon exemple de code. Ne sais pas si cela vous sera utile ou non.

overridePendingTransition(0,0);
//finish();
v.startAnimation(AnimationUtils.loadAnimation(getApplicationContext(), R.anim.fadeout));
startActivity(new Intent(ContentManagerActivity.this,Mainmenu_activity.class));
overridePendingTransition(R.anim.fadein,0);
0
Ravi A

Lorsque vous démarrez une commande d'animation dans une vue partiellement hors écran, l'animation démarre et onStartListener est appelée, mais l'animation ne s'exécute pas complètement (elle est en quelque sorte interrompue au milieu). Je suppose que, comme la vue est hors écran, elle est annulée et donc onFinish ne s'appelle pas. En guise de solution de contournement, j'ai créé mon propre écouteur d'animation qui commence par un gestionnaire et utilise postDelayed pour informer l'utilisateur de l'événement de fin d'animation. À Kotlin:

abstract class PartiallyOffScreenAnimationListener : Animation.AnimationListener, AnimationListener {
    override fun onAnimationRepeat(animation: Animation?) {
        onAnimationRepeat_(animation)
    }
    override fun onAnimationEnd(animation: Animation) {}

    override fun onAnimationStart(animation: Animation) {
        onAnimationStart_(animation)
        Handler().postDelayed({
            onAnimationEnd_(animation)
            animation.setAnimationListener(null)
        }, animation.duration + 50)
    }
}

Veuillez noter que, si l'animation ne s'exécute pas complètement, la vue risque de rester incohérente (par exemple, l'animation des paramètres de présentation peut entraîner la suppression d'un étrange facteur d'interpolateur central. Pour cette raison, la fin du rappel que la vue est dans l'état souhaité. Sinon, configurez-la manuellement.

0
johnny_crq

Faites-le comme des belows

  1. rendre l'animation finale
  2. invoquer à l'intérieur d'un gestionnaire
  3. l’auditeur doit être instancié une fois.
  4. animation claire.

par exemple view.clearAnimation ();

new Hander().post(
   run() {
final TranslateAnimation ani = new TranslateAnimation(0, 0, 0, 0);
ani.setAnimationListener(mListener);
 }
);

private Animation.AnimationListener mListener = new Animation.AnimationListener() {
}
0
Comshot

Ne définissez-vous pas une autre animation avant celle que vous attendez?

Sans contexte, il est difficile de comprendre si c'est quelque chose comme ça ... mais c'est probablement l'une des seules causes possibles.

0
neteinstein

Il se peut que quelqu'un ayant toujours ce problème et ne trouvant pas de solution - même s'il lit beaucoup de réponses stackoverflow - comme moi! 

Donc, mon cas était: j’avais utilisé animatorSet et 

  1. il n'y avait pas une seule vue que je pourrais appeler clearAnimation sur, 
  2. Je n'appelais pas mon animation par backgroundThread - ce que tu ne devrais jamais faire, btw- 

En guise de solution, j’ai appelé animatorSet.end() juste avant animatorSet.start()

0
yahya

Le retard est probablement dû à anim.setDuration (1000) ou, si vous le faites sur un thread, probablement à un changement de contexte. Essayez de manipuler le temps de retard et voyez si vous remarquez une différence.

0
devanshu_kaushik

Je suppose qu’il est appelé après un certain temps parce que vous utilisez la durée définie pour l’animation et que vous y passez 1000 ms. Il suffit de passer 0 et il ne faudra pas longtemps pour se faire appeler. 

0
success_anil