Android fondu et disparaître avec ImageView


89

J'ai des problèmes avec un diaporama que je construis.

J'ai créé 2 animations au format XML pour les fondus entrants et sortants:

fadein.xml

    <?xml version="1.0" encoding="UTF-8"?>
       <set xmlns:android="http://schemas.android.com/apk/res/android">
         <alpha android:fromAlpha="0.0" android:toAlpha="1.0" 
          android:interpolator="@android:anim/accelerate_interpolator" 
          android:duration="2000"/>
     </set>

fadeout.xml

    <?xml version="1.0" encoding="UTF-8"?>
       <set xmlns:android="http://schemas.android.com/apk/res/android">
         <alpha android:fromAlpha="1.0" android:toAlpha="0.0" 
          android:interpolator="@android:anim/accelerate_interpolator" 
          android:duration="2000"/>
     </set>

Ce que j'essaie de faire, c'est de changer les images d'une ImageView en utilisant l'effet de fondu, de sorte que l'image actuellement affichée disparaîtra et une autre disparaîtra. Étant donné que j'ai une image déjà définie, je peux fondre cette image sans problème, avec ceci:

    Animation fadeInAnimation = AnimationUtils.loadAnimation(this, R.anim.your_fade_in_anim);
    imageView.startAnimation(fadeoutAnim);

Mais ensuite, j'ai défini l'image suivante à afficher:

    imageView.setImageBitmap(secondImage);

Il apparaît juste dans l'imageView, et quand je règle l'animation, il cache l'image, le fondu dans ... Y a-t-il un moyen de résoudre ce problème, je veux dire, quand je fais imageView.setImageBitmap (secondImage); , l'image n'apparaît pas immédiatement, et uniquement lorsque l'animation en fondu est exécutée?

Réponses:


65

Pour implémenter cela de la façon dont vous avez commencé, vous devrez ajouter un AnimationListener afin de pouvoir détecter le début et la fin d'une animation. Lorsque onAnimationEnd () pour le fondu de sortie est appelé, vous pouvez définir la visibilité de votre objet ImageView sur View.INVISIBLE, basculer les images et démarrer votre animation de fondu - vous aurez également besoin d'un autre AnimationListener ici. Lorsque vous recevez onAnimationEnd () pour votre animation en fondu, définissez ImageView sur View.VISIBLE et cela devrait vous donner l'effet que vous recherchez.

J'ai déjà implémenté un effet similaire, mais j'ai utilisé un ViewSwitcher avec 2 ImageViews plutôt qu'un seul ImageView. Vous pouvez définir les animations "in" et "out" pour le ViewSwitcher avec votre fondu entrant et sortant afin qu'il puisse gérer l'implémentation d'AnimationListener. Ensuite, tout ce que vous avez à faire est d'alterner entre les 2 ImageViews.

Edit: Pour être un peu plus utile, voici un exemple rapide d'utilisation de ViewSwitcher. J'ai inclus la source complète sur https://github.com/aldryd/imageswitcher .

activity_main.xml

    <ViewSwitcher
        android:id="@+id/switcher"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:inAnimation="@anim/fade_in"
        android:outAnimation="@anim/fade_out" >

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:scaleType="fitCenter"
            android:src="@drawable/sunset" />

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:scaleType="fitCenter"
            android:src="@drawable/clouds" />
    </ViewSwitcher>

MainActivity.java

    // Let the ViewSwitcher do the animation listening for you
    ((ViewSwitcher) findViewById(R.id.switcher)).setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            ViewSwitcher switcher = (ViewSwitcher) v;

            if (switcher.getDisplayedChild() == 0) {
                switcher.showNext();
            } else {
                switcher.showPrevious();
            }
        }
    });

Merci mec! J'ai oublié le serVisible! Et merci pour le conseil de ViewSwitcher, cela semble plus facile que de tout gérer moi-même.
IPValverde

@Aldryd Est-ce plus efficace en termes de performances? Par rapport à la réponse sélectionnée (fichier XML séparé)?
Ron

@Ron Je n'ai pas vraiment comparé les performances des différentes méthodes. Comparé au décodage / travail avec des bitmaps pour les objets ImageView, je pense que l'utilisation d'un ViewSwitcher par rapport à l'implémentation d'AnimationListener vous-même ne serait pas très visible. J'ai récemment découvert que si vous utilisez l'API 11 ou une version supérieure, vous pouvez utiliser de nouvelles classes d'animation. Vous pouvez voir un exemple de crossfading 2 vues avec l'API 11 ici: developer.android.com/training/animation/crossfade.html
Aldryd

96

Je voulais atteindre le même objectif que vous, j'ai donc écrit la méthode suivante qui fait exactement cela si vous lui passez un ImageView et une liste de références aux images dessinables.

ImageView demoImage = (ImageView) findViewById(R.id.DemoImage);
int imagesToShow[] = { R.drawable.image1, R.drawable.image2,R.drawable.image3 };

animate(demoImage, imagesToShow, 0,false);  



  private void animate(final ImageView imageView, final int images[], final int imageIndex, final boolean forever) {

  //imageView <-- The View which displays the images
  //images[] <-- Holds R references to the images to display
  //imageIndex <-- index of the first image to show in images[] 
  //forever <-- If equals true then after the last image it starts all over again with the first image resulting in an infinite loop. You have been warned.

    int fadeInDuration = 500; // Configure time values here
    int timeBetween = 3000;
    int fadeOutDuration = 1000;

    imageView.setVisibility(View.INVISIBLE);    //Visible or invisible by default - this will apply when the animation ends
    imageView.setImageResource(images[imageIndex]);

    Animation fadeIn = new AlphaAnimation(0, 1);
    fadeIn.setInterpolator(new DecelerateInterpolator()); // add this
    fadeIn.setDuration(fadeInDuration);

    Animation fadeOut = new AlphaAnimation(1, 0);
    fadeOut.setInterpolator(new AccelerateInterpolator()); // and this
    fadeOut.setStartOffset(fadeInDuration + timeBetween);
    fadeOut.setDuration(fadeOutDuration);

    AnimationSet animation = new AnimationSet(false); // change to false
    animation.addAnimation(fadeIn);
    animation.addAnimation(fadeOut);
    animation.setRepeatCount(1);
    imageView.setAnimation(animation);

    animation.setAnimationListener(new AnimationListener() {
        public void onAnimationEnd(Animation animation) {
            if (images.length - 1 > imageIndex) {
                animate(imageView, images, imageIndex + 1,forever); //Calls itself until it gets to the end of the array
            }
            else {
                if (forever){
                animate(imageView, images, 0,forever);  //Calls itself to start the animation all over again in a loop if forever = true
                }
            }
        }
        public void onAnimationRepeat(Animation animation) {
            // TODO Auto-generated method stub
        }
        public void onAnimationStart(Animation animation) {
            // TODO Auto-generated method stub
        }
    });
}

1
Pour montrer comment les animations de fondu entrant et sortant peuvent être définies dans le code, vous monsieur, obtenez mon vote
Herr Grumps

Vous pouvez également compter les répétitions et effectuer un décompte Array.lenght% dans votre méthode
onAnimationRepeat

2
@Crocodile Pensez-vous qu'il pourrait y avoir un problème de mémoire dans votre code. Laissez l'activité avec le code ci-dessus continuer à s'exécuter. Cela créera tellement d'objets AnimationSet. Y a-t-il une possibilité qu'il plante avec outOfMemory après un certain temps?
Gem du

1
Vous monsieur êtes une bouée de sauvetage!
faizanjehangir

2
@Gem - pourquoi cette allocation particulière causerait-elle un problème? Chaque fois que imageView.setAnimation (animation) est appelée, toute référence à une animation précédente est perdue, de sorte que le garbage collector puisse supprimer cet objet précédent. AFAIK, pas un problème de mémoire.
greg7gkb

46

Avez-vous pensé à utiliser TransitionDrawable au lieu d'animations personnalisées? https://developer.android.com/reference/android/graphics/drawable/TransitionDrawable.html

Une façon d'atteindre ce que vous recherchez est:

// create the transition layers
Drawable[] layers = new Drawable[2];
layers[0] = new BitmapDrawable(getResources(), firstBitmap);
layers[1] = new BitmapDrawable(getResources(), secondBitmap);

TransitionDrawable transitionDrawable = new TransitionDrawable(layers);
imageView.setImageDrawable(transitionDrawable);
transitionDrawable.startTransition(FADE_DURATION);

2
C'est la meilleure réponse à cette question.
DragonT

Désolé pour le bump, mais si j'utilise cela dans un gestionnaire et que je l'ai dans une boucle permanente, l'application perd des performances dans une autre animation de fondu entrant / sortant constant que j'ai. Des recommandations?
James

TransitionDrawable ne peut gérer que deux images / calques.
Signcodeindie

Transition drawable provoque AppNotIdleException lors de l'exécution du test espresso
PK Gupta


3

Sur la base de la solution d'Aladin Q, voici une fonction d'aide que j'ai écrite, qui changera l'image dans une vue d'image tout en exécutant un petit fondu / fondu dans l'animation:

public static void ImageViewAnimatedChange(Context c, final ImageView v, final Bitmap new_image) {
        final Animation anim_out = AnimationUtils.loadAnimation(c, android.R.anim.fade_out); 
        final Animation anim_in  = AnimationUtils.loadAnimation(c, android.R.anim.fade_in); 
        anim_out.setAnimationListener(new AnimationListener()
        {
            @Override public void onAnimationStart(Animation animation) {}
            @Override public void onAnimationRepeat(Animation animation) {}
            @Override public void onAnimationEnd(Animation animation)
            {
                v.setImageBitmap(new_image); 
                anim_in.setAnimationListener(new AnimationListener() {
                    @Override public void onAnimationStart(Animation animation) {}
                    @Override public void onAnimationRepeat(Animation animation) {}
                    @Override public void onAnimationEnd(Animation animation) {}
                });
                v.startAnimation(anim_in);
            }
        });
        v.startAnimation(anim_out);
    }

3

vous pouvez le faire par deux simples points et changer votre code

1.Dans votre dossier xml dans le dossier anim de votre projet, définissez la durée du fondu entrant et sortant non égale

2.Dans votre classe java avant le début de l'animation en fondu, définissez la deuxième visibilité d'imageView.Autant, après le début de l'animation en fondu, définissez la deuxième visibilité imageView que vous souhaitez rendre visible

fadeout.xml

<alpha
    android:duration="4000"
    android:fromAlpha="1.0"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:toAlpha="0.0" />

fadein.xml

<alpha
    android:duration="6000"
    android:fromAlpha="0.0"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:toAlpha="1.0" />

Dans votre classe java

Animation animFadeOut = AnimationUtils.loadAnimation(this, R.anim.fade_out);
    ImageView iv = (ImageView) findViewById(R.id.imageView1);
    ImageView iv2 = (ImageView) findViewById(R.id.imageView2);
    iv.setVisibility(View.VISIBLE);
    iv2.setVisibility(View.GONE);
    animFadeOut.reset();
    iv.clearAnimation();
    iv.startAnimation(animFadeOut);

    Animation animFadeIn = AnimationUtils.loadAnimation(this, R.anim.fade_in);
    iv2.setVisibility(View.VISIBLE);
    animFadeIn.reset();
    iv2.clearAnimation();
    iv2.startAnimation(animFadeIn);

3

Pour un fondu d'entrée et de sortie infini

AlphaAnimation fadeIn=new AlphaAnimation(0,1);

AlphaAnimation fadeOut=new AlphaAnimation(1,0);


final AnimationSet set = new AnimationSet(false);

set.addAnimation(fadeIn);
set.addAnimation(fadeOut);
fadeOut.setStartOffset(2000);
set.setDuration(2000);
imageView.startAnimation(set);

set.setAnimationListener(new Animation.AnimationListener() {
    @Override
    public void onAnimationStart(Animation animation) { }
    @Override
    public void onAnimationRepeat(Animation animation) { }
    @Override
    public void onAnimationEnd(Animation animation) {
        imageView.startAnimation(set);
    }
});

1

J'utilise ce genre de routine pour enchaîner les animations par programmation.

    final Animation anim_out = AnimationUtils.loadAnimation(context, android.R.anim.fade_out); 
    final Animation anim_in  = AnimationUtils.loadAnimation(context, android.R.anim.fade_in); 

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

        @Override
        public void onAnimationRepeat(Animation animation) {}

        @Override
        public void onAnimationEnd(Animation animation)
        {
            ////////////////////////////////////////
            // HERE YOU CHANGE YOUR IMAGE CONTENT //
            ////////////////////////////////////////
            //ui_image.setImage...

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

                @Override
                public void onAnimationRepeat(Animation animation) {}

                @Override
                public void onAnimationEnd(Animation animation) {}
            });

            ui_image.startAnimation(anim_in);
        }
    });

    ui_image.startAnimation(anim_out);

1

La meilleure et la plus simple, pour moi, c'était la suivante.

-> Créez simplement un thread avec Handler contenant sleep ().

private ImageView myImageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_shape_count); myImageView= (ImageView)findViewById(R.id.shape1);
    Animation myFadeInAnimation = AnimationUtils.loadAnimation(this, R.anim.fadein);
    myImageView.startAnimation(myFadeInAnimation);

    new Thread(new Runnable() {
        private Handler handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                Log.w("hendler", "recived");
                    Animation myFadeOutAnimation = AnimationUtils.loadAnimation(getBaseContext(), R.anim.fadeout);
                    myImageView.startAnimation(myFadeOutAnimation);
                    myImageView.setVisibility(View.INVISIBLE);
            }
        };

        @Override
        public void run() {
            try{
                Thread.sleep(2000); // your fadein duration
            }catch (Exception e){
            }
            handler.sendEmptyMessage(1);

        }
    }).start();
}

1

C'est probablement la meilleure solution que vous obtiendrez. Simple et facile. Je l'ai appris sur udemy. Supposons que vous ayez deux images ayant respectivement l'id1 et l'id2 de l'ID d'image et que la vue de l'image soit actuellement définie comme id1 et que vous souhaitiez la remplacer par l'autre image à chaque fois que quelqu'un clique dessus. Voici donc le code de base dans MainActivity.javaFile

int clickNum=0;
public void click(View view){
clickNum++;
ImageView a=(ImageView)findViewById(R.id.id1);
ImageView b=(ImageView)findViewById(R.id.id2);
if(clickNum%2==1){
  a.animate().alpha(0f).setDuration(2000); //alpha controls the transpiracy
}
else if(clickNum%2==0){
  b.animate().alpha(0f).setDuration(2000); //alpha controls the transpiracy
}

}

J'espère que cela aidera sûrement

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.