Android pojawia się i zanika dzięki ImageView


89

Mam problemy z pokazem slajdów, który tworzę.

Stworzyłem 2 animacje w XML do pojawiania się i zanikania:

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>

To, czego nie chcę zrobić, to zmienić obrazy z ImageView przy użyciu efektu zanikania, aby aktualnie wyświetlany obraz zniknął, a inny zniknął. Biorąc pod uwagę, że mam już ustawiony obraz, mogę zanikać ten obraz bez problem, z tym:

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

Ale potem ustawiłem następny obraz do wyświetlenia:

    imageView.setImageBitmap(secondImage);

Po prostu pojawia się w imageView, a kiedy ustawiam animację, ukrywa obraz, zanika go ... Czy jest jakiś sposób, aby to naprawić, mam na myśli, kiedy robię imageView.setImageBitmap (secondImage); polecenie, obraz nie pojawia się od razu, a dopiero po wykonaniu animacji zanikania?

Odpowiedzi:


66

Aby zaimplementować to w sposób, w jaki zacząłeś, musisz dodać AnimationListener , abyś mógł wykryć początek i koniec animacji. Gdy wywoływana jest funkcja onAnimationEnd () dla zanikania, możesz ustawić widoczność obiektu ImageView na View.INVISIBLE, przełączać obrazy i rozpocząć animację zanikania - będziesz potrzebować także innego elementu AnimationListener. Kiedy otrzymasz onAnimationEnd () dla animacji zanikania, ustaw ImageView na View.VISIBLE i to powinno dać ci efekt, którego szukasz.

Zaimplementowałem podobny efekt wcześniej, ale użyłem ViewSwitcher z 2 ImageViews zamiast pojedynczego ImageView. Możesz ustawić animacje „wejścia” i „wyjścia” dla ViewSwitcher za pomocą pojawiania się i zanikania, aby mógł zarządzać implementacją AnimationListener. Następnie wszystko, co musisz zrobić, to przełączać się między 2 widokami obrazu.

Edycja: aby być nieco bardziej użytecznym, oto szybki przykład korzystania z ViewSwitcher. Załączam pełne źródło na 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();
            }
        }
    });

Dzięki stary! Zapomniałem o serVisible! I dzięki za wskazówkę ViewSwitcher, wydaje się to łatwiejsze niż zajmowanie się wszystkim samemu.
IPValverde

@Aldryd Czy jest to bardziej wydajne pod względem wydajności? W porównaniu z wybraną odpowiedzią (osobny plik XML)?
Ron

@Ron Tak naprawdę nie porównywałem wydajności różnych metod. W porównaniu do dekodowania / pracy z mapami bitowymi dla obiektów ImageView, pomyślałbym, że użycie ViewSwitcher w porównaniu z samodzielnym wdrożeniem AnimationListener nie byłoby zbyt zauważalne. Niedawno dowiedziałem się, że jeśli używasz API 11 lub nowszego, możesz użyć nowych klas Animation. Możesz zobaczyć przykład przenikania 2 widoków z API 11 tutaj: developer.android.com/training/animation/crossfade.html
Aldryd,

96

Chciałem osiągnąć ten sam cel, co ty, więc napisałem następującą metodę, która robi dokładnie to, jeśli przekażesz jej ImageView i listę odniesień do rysunków graficznych.

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
Za pokazanie, jak animacje pojawiania się i wygaszania można zdefiniować w kodzie, proszę pana, zdobądź mój głos
Herr Grumps

Możesz także policzyć powtórzenia i zrobić Array. Długość% liczyć w metodzie
onAnimationRepeat

2
@Crocodile Czy uważasz, że w Twoim kodzie może występować problem z pamięcią? Niech aktywność z powyższym kodem będzie trwała. Stworzy tak wiele obiektów AnimationSet. Czy istnieje możliwość, że po pewnym czasie ulegnie awarii bez outOfMemory?
Gem

1
Pan jest ratownikiem!
faizanjehangir

2
@Gem - dlaczego ta konkretna alokacja miałaby powodować problem? Za każdym razem, gdy wywoływana jest funkcja imageView.setAnimation (animacja), wszelkie odwołanie do poprzedniej animacji jest tracone, więc moduł odśmiecania pamięci mógłby usunąć ten poprzedni obiekt. AFAIK, nie problem z pamięcią.
greg7gkb

46

Czy myślałeś o użyciu TransitionDrawable zamiast niestandardowych animacji? https://developer.android.com/reference/android/graphics/drawable/TransitionDrawable.html

Jednym ze sposobów osiągnięcia tego, czego szukasz, jest:

// 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
To najlepsza odpowiedź na to pytanie.
DragonT

Przepraszam za zderzanie się, ale jeśli użyję tego w Handlerze i mam to w stałej pętli, aplikacja traci wydajność w innej stałej animacji zanikania / zanikania, którą mam. Jakieś zalecenia?
James

TransitionDrawable może obsługiwać tylko dwa obrazy / warstwę.
Signcodeindie

Przejście do wyciągnięcia powoduje wyjątek AppNotIdleException podczas uruchamiania testu espresso
PK Gupta


3

W oparciu o rozwiązanie Aladin Q, oto napisana przeze mnie funkcja pomocnicza, która zmieni obraz w widoku obrazu podczas wykonywania niewielkiej animacji zanikania / zanikania:

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

możesz to zrobić za pomocą dwóch prostych punktów i zmienić swój kod

W folderze xml in anim projektu ustaw różne czasy trwania pojawiania się i zanikania

2. w klasie java przed rozpoczęciem animacji zanikania ustaw drugi imageView visibility Gone, a następnie po rozpoczęciu animacji fade out ustaw drugi imageView visibility, który chcesz zaniknąć jako widoczny

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

W twojej klasie 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

Dla nieskończonego wejścia i wyjścia

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

Używam tego rodzaju procedury do programowego łączenia animacji w łańcuchy.

    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

Najlepszym i najłatwiejszym sposobem było dla mnie to ...

-> Po prostu utwórz wątek z obsługą zawierającą funkcję 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

To prawdopodobnie najlepsze rozwiązanie, jakie otrzymasz. Proste i łatwe. Nauczyłem się tego na udemy. Załóżmy, że masz dwa obrazy mające odpowiednio id obrazu id1 i id2, a obecnie widok obrazu jest ustawiony jako id1 i chcesz go zmienić na inny obraz za każdym razem, gdy ktoś kliknie. To jest podstawowy kod w MainActivity.javapliku

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
}

}

Mam nadzieję, że to na pewno pomoże

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.