John Ernest Guadalupe
John Ernest Guadalupe

Reputation: 6629

How would you disable user clicks while fragment is transitioning?

I want my fragment to not receive any clicks on the views while the fragment transition animation is not yet finished. It is just a simple fade. But things get wonky when I immediately press any view while the next fragment is fading in.

Any thoughts how to achieve this?

Upvotes: 3

Views: 2052

Answers (3)

Derek Fung
Derek Fung

Reputation: 8211

This is actually used in my own app. The idea is very simple, it just works, but needs quite a lot of additional coding.

The idea is very simple, use a boolean variable to maintain whether the screen should be locked, let's call it screenLocked. I do not actually block the click, but let the click do nothing.

For those actions which takes time, set screenLocked to true before start working, and set it back to false when the task is finished. Also you have to add checking on screenLocked before any action is done.

Another difficulty of the this method is that you need to have clear end point of your tasks. Using Fragment transition as an example, if the backstack is poped, there has no actual callback notifying you, for this case. To handle this, I would set another flag releaseOnResume before starting Fragment transition, and in onResume, I would use this flag to check if I should set screenLocked back to false.

Other solutions I have tried but not used:

Before I settled with the method I just mentioned, I have tried setEnabled, setClickable, or any UI based blocking, e.g. add a FrameLayout on top and capture all touch events.

These methods are not bad, especially given that they are easy to implement.

The only problem is that, onClick events can be queued due to double tapping, when you are handling the first onClick event, actually there could be another one queued up, even if you do any UI changes immediately to block any further clicks, you can't stop the next onClick event to come because it is queued already.

Hope this helps.

Upvotes: 2

John Ernest Guadalupe
John Ernest Guadalupe

Reputation: 6629

In the end I used something like this. I created a parent class for all my fragments and overriden the OnCreateAnimation method which is called on every animation.

@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
    //Check if the superclass already created the animation
    Animation anim = super.onCreateAnimation(transit, enter, nextAnim);

    //If not, and an animation is defined, load it now
    if (anim == null && nextAnim != 0) {
        anim = AnimationUtils.loadAnimation(getActivity(), nextAnim);
    }

    //If there is an animation for this fragment, add a listener.
    if (anim != null) {
        anim.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                isAnimationFinished = false;
            }

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

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
    }

    return anim;
}

The isAnimationFinished variable is a public variable that can be used by the calling activity and the child classes

Upvotes: 1

user3956566
user3956566

Reputation:

I use a countdown timer. I manage this through the ontouch listener.

I create a method that manages the creation of the timer. I call it in the ontouch event. I use two methods (this is optional, but good for extensibility) to handle button enabling and disabling. I then use these methods with the timer to enable and disable the button.

See my code snippet.

In oncreate:

 @Override protected void onCreate(Bundle savedInstanceState) {
   /.../
    button.setOnTouchListener(new View.OnTouchListener() {
        @Override public boolean onTouch(View v, MotionEvent event) {
            disableButton(button);
            countDwn1();
            /... time to do whatever you need..

            // custom methods...

            fragment = new MyFragAddFragment();
            replaceFragment(fragment);
            return false;
        }
    });

Methods:

public void countDwn1() {
    CountDownTimer countDownTimer = new CountDownTimer(2000, 1000) {
        public void onTick(long millisUntilFinished) {
        }

        public void onFinish() {
            enableButton(button);
        }
    }.start();
}


public void disableButton(Button button) {
    button.setEnabled(false);
}

public void enableButton(Button button) {
    button.setEnabled(true);
}

You can extend this method to include passing the button as a parameter into the timer, for extensibility.

Upvotes: 1

Related Questions