slida
slida

Reputation: 43

Playing frame animations in a sequence. Android

Ok, somewhat similar questions have been asked, but there is no answer that has made any difference in solving my problem. I've tried Thread.sleep, and also the delayed runnable. Using a Handler, etc.

I want to display a sequence of frame animations(AnimationDrawable) using the same imageview and changing the background animations as needed. The user inputs a series of 5 animations and can then play them back(if my program worked). Once the animations are selected I use a for loop that contains if statements and a switch statement to select that whatever animations were chosen and play them back.

As you might imagine, this doesn't work correctly, as the program whips through the for loop and only the first and last animation actually play. Here is the jist of the code:

for(i=0;i<5;i++){
    if(conditions){
        view.setBackground(chosenAnimation);
        ((AnimationDrawable)view.getBackground().start();
    }
}

So as I said, I have tried Thread.sleep(), that doesn't do what I'm looking for. I have tried using the Handler class and putting a delay on the runnable. I have tried this:

view.postDelayed(new Runnable() {
    @Override 
    public void run() {
        //works the same without this line
        ((AnimationDrawable)view.getBackground()).stop();
        ((AnimationDrawable)view.getBackground()).start();
    }
}, 1000);

None of these things do anything at all except add pauses before it does the exact same thing it did before I added this stuff. I have meticulously debugged the code and everything is working correctly. The animations have all been individually tested.

As I said, similar questions have been asked and answered and nothing offered does what I want, which is for the program to wait until one animation is finished before it runs through the for loop again.

I'd like to state again that this is a series of frame animations using AnimationDrawable and the same imageview each time. Thanks in advance!

Upvotes: 3

Views: 1719

Answers (3)

Cheng
Cheng

Reputation: 785

You can simply using for loop and you dont need 3rd party library.

Lets say you have a LinearLayout(or any viewgroup) that you want to add these buttons dynamically. You can do something like this:

for (int i = 0; i < buttons.size(); i++) {
   Button button = new Button(context); 
   new Handler().postDelayed(new Runnable() {
       @Override 
       public void run() { 
          linearLayout.addView(button);
          animateButton(button);
       } 
   }, BUTTON_DELAY_DURATION * i);
} 

Upvotes: 1

slida
slida

Reputation: 43

Thanks to some assistance from dtx12 here, I have realized my problem, and I thought I'd leave some code here since I am not good enough to completely understand their example.

I didn't quite understand what I was doing making new threads, and it turns out I was just making 5 threads that all went off at the same time, as dtx12 explained and I eventually understood. So here's a basic way of doing this:

public void playAnimation(){
    // in dtx12's answer they demonstrate how to get the
    // exact duration of any give animation using 
   //getNumberOfFrames() and getDuration() methods of 
   // AnimationDrawable but I am just hardcoding it for simplicity

    int duration=2000;
   // This is to keep track of the animations which are in an array, I use this variable in run()
     order=1;

                  for(int i=0;i<5;i++){
  //play the first animation

                      if(i==0){
                          view.setBackground(animation[0]);
                            animation[i].stop();
                            animation[i].start();

                      }
                      else{
//Now set up the next animation to play after 2000ms, the next  after 4000ms, etc.
//You are supposed to use a handler if you want to change the view in the main thread.
//it is very easy: Handler handler=new Handler():

                          handler.postDelayed(new Runnable(){

                            @Override
                            public void run() {

                                 view.setBackground(animation[order]);
                                animation[order].stop();
                                animation[order].start();
                                order++;


                            }

                          }, duration);
       //dtx12 suggestion of multiplying by i is probably smarter                 
                          duration+=2000;

                      }

                  }



                }

So there you have it. Not the most elegant display of coding, but it gets the basic job done. You can't use 'i' in the run() method because it is an anonymous inner class, and if you assign the value of 'i' to another variable in the loop, it will be '4', by the time the other threads execute. So, I just did some counting inside of run to make sure everything fired in order.

Upvotes: 0

dtx12
dtx12

Reputation: 4488

All your animations will be started with the same delay, you should increase this delay by multiplying it by i for example. You also can count duration of every animation programmatically and increase delay as you need it.

I just tried to achieve what you want and had no problems, although my example uses 3rd party library, it's not necessary.

package com.example.masktest.app;

import android.animation.Animator;
import android.graphics.drawable.AnimationDrawable;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;

import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicReference;

import static com.dtx12.android_animations_actions.actions.Actions.*;


public class MainActivity extends AppCompatActivity {

    ImageView imageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageView = (ImageView) findViewById(R.id.imageView);
        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                playAnimation();
            }
        });
    }

    private void playAnimation() {
        final AnimationDrawable firstDrawable = (AnimationDrawable) ContextCompat.getDrawable(MainActivity.this, R.anim.anim_android);
        final AnimationDrawable secondDrawable = (AnimationDrawable) ContextCompat.getDrawable(MainActivity.this, R.anim.anim_android_2);
        final AtomicReference<Integer> cpt = new AtomicReference<>(0);
        Animator sequence = repeat(6, sequence(run(new Runnable() {
            @Override
            public void run() {
                if (imageView.getDrawable() instanceof AnimationDrawable) {
                    ((AnimationDrawable) imageView.getDrawable()).stop();
                }
                imageView.setImageDrawable(cpt.get() % 2 == 0 ? secondDrawable : firstDrawable);
                ((AnimationDrawable) imageView.getDrawable()).start();
                cpt.set(cpt.get() + 1);
            }
        }), delay(countAnimationDuration(secondDrawable))));
        play(sequence, imageView);
    }

    private float countAnimationDuration(AnimationDrawable drawable) {
        int duration = 0;
        for (int i = 0; i < drawable.getNumberOfFrames(); i++) {
            duration += drawable.getDuration(i);
        }
        return duration / 1000f;
    }
}

Upvotes: 2

Related Questions