Search code examples
javaandroidanimationimageswitcher

Android - Sequential looped animation for N frames whilst changing ImageViews


I want to create a simple '3-2-1' countdown using three animated ImageViews that rotate and zoom onto the screen, triggered by a Button. I have no problem with the animation for a single ImageView, but I cannot get a sequential execution of the 3... then 2... then 1 frames. Could anyone help? Here's the simplified layout XML for the MainActivity

<Button
android:id="@+id/go_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/rotate"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="120dp"
android:text="GO!" />

<ImageSwitcher
android:id="@+id/imageSwitcher"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="220dp" />

Here's one of my XML-configured animations (this one zooms the ImageView out):

<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator"
android:startOffset="0"
android:fromXScale="5.0"
android:toXScale="1.0"
android:fromYScale="5.0"
android:toYScale="1.0"
android:duration="600"
android:pivotX="50%"
android:pivotY="50%" >
</scale>

and here is the MainActivity Java file:

public class MainActivity extends ActionBarActivity {

private Button animateCountdown;
private ImageSwitcher myImageSwitcher;
private static final int[] IMAGES = { R.drawable.icon_3, R.drawable.icon_2, R.drawable.icon_1};
private int currentIMAGESIndex = 0;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    myImageSwitcher = (ImageSwitcher) findViewById(R.id.imageSwitcher);
    myImageSwitcher.setFactory(new ViewSwitcher.ViewFactory() {
        @Override
        public View makeView() {
            ImageView imageView = new ImageView(MainActivity.this);
            return imageView;
        }
    });

    animateCountdown = (Button) findViewById(R.id.go_button);
    animateCountdown.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            runCountdownAnimation(view);
        }
    });
}

private void runCountdownAnimation(View view){

    //Using an animation set, to allow for concurrent animations.
    AnimationSet animationSet = getCountdownAnimationSet();

   Animation fadeOutAnimation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.fade_out);

    myImageSwitcher.setInAnimation(animationSet);
    myImageSwitcher.setOutAnimation(fadeOutAnimation);

    while(currentIMAGESIndex < (IMAGES.length - 1)){
         myImageSwitcher.setImageResource(IMAGES[currentIMAGESIndex]);
         currentIMAGESIndex++;
         myImageSwitcher.postDelayed(this, 1000);
     }

I'm using an animation set, in order to run a zoom, alpha and rotate animation concurrently. So far, I have tried to change the ImageView source and restart the animation from within the onAnimationEnd() of an Animation listener... but no luck. Just looping over the images in the array doesn't work either.

How can I loop the animation 3 times, but change the ImageView at the end of each iteration? At the end of the final iteration, I want to create an Intent and load a new activity.

Thanks in advance :)


Update... here's the getCountDownAnimationSet() code:

private AnimationSet getCountdownAnimationSet(){
    Animation rotateAnimation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.rotate);
    Animation fadeAnimation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.fade_in);
    Animation zoomAnimation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.zoom);

    AnimationSet animationSet = new AnimationSet(false); //false means don't share interpolators
    animationSet.addAnimation(rotateAnimation);
    animationSet.addAnimation(fadeAnimation);
    animationSet.addAnimation(zoomAnimation);

    return animationSet;
}

Solution

  • OK... so I sorted this. I used an Android countdown timer

    private void initialiseCountdownTimer(long timerDuration, long timerInterval){
        countDownTimer = new CountDownTimer(timerDuration, timerInterval) {
            @Override
            public void onTick(long millisUntilFinished) {
                if(!countdownImageStack.empty()){
                    countdownImage.setImageResource(countdownImageStack.pop());
                    countdownImage.startAnimation(countdownAnimationSet);
    
                }
    
            }
    

    I used a stack containing references to the images (used as keyframes in the animation). On each tick, I switched the image file by popping the stack. I started the countdown Timer via an onClick() method.

    Result? No nasty concurrency issues - but I still used the Animation set in order to rotate and zoom at the same time.