0

I have a ViewFlipper with 3 views each one display a different Layout , each child have a next button.

when clicked it animated the transition to the other child using "ViewFlipper" . What i want when the user click a button an animation starts i want to disable all button to prevent Animation collision . what i faced when i click multiple time very fast on a button i restart the animation over .

what i have tried so far

now by connection all animtion object to one Listener and keep a boolean falg to indicate if an animation is running or not . and in the onTouchEvent() of the Activity if the flag is true i return true else i return the super method. this flag set to true in the onAnimationStart callback and set to false in onAnimationEnd didnt work. and when i log the method calls it shows that onAnimationEnd called before the animation ends completely.

Any suggestion to solve this problem ??

code

public class CreateNewKhtmehActivity extends FragmentActivity implements
        OnClickListener, AnimationListener {    
    AtomicBoolean isAnimationPlaying = new AtomicBoolean(false);
    // phase 1
    Button phase1_continue_button;
    // phase 2      
    Button phase2_continue_button;
    // phase 3
    Button phase3_start_session;

    /** Called when the activity is first created. */
    ViewFlipper flipper;
    Animation flip_in_from_left, flip_in_from_right, flip_out_to_left,
            flip_out_to_right, stay_still;

    RelativeLayout phase1;
    RelativeLayout phase2;
    RelativeLayout phase3;


    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.create_new_session);

        /*
         * 
         * phase 1
         */
        phase1 = (RelativeLayout) findViewById(R.id.phase1);
        setUpUIForPhase1(phase1);

        // phase2
        phase2 = (RelativeLayout) findViewById(R.id.phase2);
        setUpUIForPhase2(phase2);

        // phase 3
        phase3 = (RelativeLayout) findViewById(R.id.phase3);
        setUpUIForPhase3(phase3);
        /*
         * Animation
         */

        flipper = (ViewFlipper) findViewById(R.id.main_flipper);
        flipper.setDrawingCacheEnabled(true);

        flipper.setDisplayedChild(2);
        flip_in_from_left = AnimationUtils.loadAnimation(
                getApplicationContext(), R.anim.push_in_from_left);
        flip_in_from_left.setAnimationListener(this);
        flip_in_from_right = AnimationUtils.loadAnimation(
                getApplicationContext(), R.anim.push_in_from_right);
        flip_in_from_right.setAnimationListener(this);
        flip_out_to_left = AnimationUtils.loadAnimation(
                getApplicationContext(), R.anim.push_out_to_left);
        flip_out_to_left.setAnimationListener(this);
        flip_out_to_right = AnimationUtils.loadAnimation(
                getApplicationContext(), R.anim.push_out_to_right);
        flip_out_to_right.setAnimationListener(this);
        stay_still = AnimationUtils.loadAnimation(getApplicationContext(),
                R.anim.stay_still);

        stay_still.setAnimationListener(this);


    }

    private void setUpUIForPhase3(View v) {

        phase3_start_session = (Button) v
                .findViewById(R.id.start_session_button);
        phase3_start_session.setOnClickListener(this);
     }


    private void setUpUIForPhase2(View v) {

        phase2_continue_button = (Button) v
                .findViewById(R.id.phase2_continue_button);
        phase2_continue_button.setOnClickListener(this);
    }
    private void setUpUIForPhase1(View v) {
        phase1_continue_button = (Button) phase1
                .findViewById(R.id.phase1_continue_Button);
        phase1_continue_button.setOnClickListener(this);

    }

    public void onBackPressed() {

        int currentChild = flipper.getDisplayedChild();
        switch (currentChild) {
        // first displayed child
        case 0:
            flipNext();
            if (this.move_from_phase1_to_phase3) {
                flipper.setDisplayedChild(2);

            } else {
                spinner_from.invalidate();
                spinner_to.invalidate();
                flipper.setDisplayedChild(1);
            }

            break;
        case 1:
            flipNext();
            flipper.setDisplayedChild(2);
            break;
        default:
            super.onBackPressed();
        }

    }

    public void onClick(View v) {
        System.out.println("onClick");

        switch (v.getId()) {
        case R.id.phase1_continue_Button:
            spinner_from.invalidate();
            spinner_to.invalidate();
            flipPrevious();
            if (this.move_from_phase1_to_phase3) {
                flipper.setDisplayedChild(0);
            } else {
                flipper.setDisplayedChild(1);
            }

            break;
        case R.id.phase2_continue_button:
            flipPrevious();
            flipper.setDisplayedChild(0);

            break;

        case R.id.start_session_button:
            insetNewSession();

            break;

        }

    }

    // Methods concerning the flip

    public void flipNext() {

        flipper.setInAnimation(flip_in_from_right);

        flipper.setOutAnimation(flip_out_to_left);

    }

    public void flipPrevious() {

        flipper.setInAnimation(flip_in_from_left);

        flipper.setOutAnimation(flip_out_to_right);

    }

    public void stayStill() {

        flipper.setInAnimation(stay_still);

        flipper.setOutAnimation(stay_still);

    }

     .......
     ........

     @Override
     public boolean onTouchEvent(MotionEvent event) {
     System.out.println("isAnimationPlaying " + isAnimationPlaying.get());
     if (isAnimationPlaying.get()) {
     return true;
     } else {
     return super.onTouchEvent(event);
     }

    // }

    public void onAnimationEnd(Animation animation) {
        System.out.println("onAnimationEnd");
            isAnimationPlaying.set(true);
    }

    public void onAnimationRepeat(Animation animation) {
        System.out.println("onAnimationRepeat");
    }

    public void onAnimationStart(Animation animation) {
        System.out.println("onAnimationStart");
        isAnimationPlaying.set(false);

    }

}
confucius
  • 13,127
  • 10
  • 47
  • 66
  • can you post yoru animating code? – FoamyGuy Aug 07 '12 at 19:03
  • 1
    I am not sure why it wouldn't work as is, but Arhimed's answer should do the trick. Also side note (unrelated to your issue) you should use `CreateNewKhtmehActivity.this` instead of `getApplicationContext()` That method is unnecessary here and prone to memory leaks. – FoamyGuy Aug 07 '12 at 19:38
  • AFAIK the Activity context can cause memory leak what cases should ApplicationContext leaks memory , can you provide me with a link or an article for that? – confucius Aug 07 '12 at 19:54
  • In this case you just call a static util method that uses a `Context` instance just to load a resource. It does not keep a reference on the passed in `Context` instance. Since all this happens on the main UI thread inside of `onCreate` it is safe to use an instance of the `Activity` here. – Vit Khudenko Aug 07 '12 at 20:14
  • 1
    @Nammari [see this answer by CommonsWare](http://stackoverflow.com/questions/7298731/when-to-call-activity-context-or-application-context/7298955#7298955) Which also links to another answer given by Ms. Hackborn of the Android dev team. – FoamyGuy Aug 07 '12 at 21:59

1 Answers1

2

I can suggest 2 improvements (however I had no chance to try it with ViewFlipper so I can't guarantee it will do the desired trick):

1) Set the flag to true even before animation starts (let it be the first thing to do inside of the button click listener).

2) In attempt to extend the time the flag is true, don't set it to false inside of the onAnimationEnd. Instead of direct setting let the onAnimationEnd to create a Runnable (that would set the flag to false) and post it to be run the next time UI thread is ready to do it. So instead of setting it right now, it will be done later (once the current UI thread working units have been run). Just in case you don't know - internally there is a message queue of Runnables to be executed on the main UI thread. And onAnimationEnd is run on the main UI thread as a part of the current UI changes. Once the UI changes are done your Runnable will be processed. This way you postpone setting to false. It should be smth like this:

@Override
protected void onAnimationEnd() {
    Runnable resetter = new Runnable() {
        @Override
        public void run() {
            yourFlag = false;
        }
    };
    // handler is an instance of android.os.Handler,
    // it should be created somewhere before on the main UI thread
    // using the simplest Handler() constructor
    handler.post(resetter);
}
Vit Khudenko
  • 28,288
  • 10
  • 63
  • 91
  • note: if you need to squeek out even more time you can use `handler.post(resetter,100);` where `100` is a millisecond value that will delay the runnable by that many milliseconds. So in this case it would delay it by an extra 1/10 of a second, play with the value until it is where you want it. – FoamyGuy Aug 07 '12 at 19:37
  • please see the UPDATE section – Vit Khudenko Aug 07 '12 at 19:43
  • @Arhimed i used your trick with one listener for all Animation since one animation can run at a time no overlapping . anyway could you tell me why should i create new animation each time i need need them ??? what is the hidden story behind that?! – confucius Aug 07 '12 at 20:27
  • Actually I decided to be top careful just from a quick glance on your code (there was not a deep analysis). If you are sure animations don't overlap, then it should be Ok as is. – Vit Khudenko Aug 07 '12 at 20:46
  • I removed the UPDATE section because it appeared to be irrelevant. Sorry for this mess. – Vit Khudenko Aug 07 '12 at 20:48