Search code examples
androidanimationfragmenttransactionanimator

Wait for FragmentTransaction animation to finish


I have an Activity with an EditText and a Button. As soon as there is a number entered in the EditText and the Button is clicked, a Fragment (that covers up the whole screen) is being shown for two seconds, and then removed. Both Transactions have showCustomAnimations() called. In code, after I call commit() on the remove FragmentTransaction, the soft keyboard is shown via showSoftInput().

The problem I face: While the remove animation is running and the Fragment already slid out of the screen by 80%, the softkeyboard pops up. Now if you quickly enter a number and press the button, the app crashes, because the previous animation still was running.

I already saw several answers, but I found them to not be sufficient or working. Any call for commitNow() or executePendingTransitions() doesn't help, as the Transaction is committed, the problem is, that the animation is still running afterwards. The method getVisibleUserHint()to check whether an Activity is visible is deprecated.

Code:

TrueFragment trueFragment = new TrueFragment();

fabNext.setOnClickListener(new View.OnClickListener() {  //fabNext is my button
        @Override
        public void onClick(View v) {

            fabNext.setEnabled(false);  //blocking button to avoid clicking (... further code for this...)
            inputManager.hideSoftInputFromWindow(constraintLayoutCalculating.getWindowToken(), 0);  //hide Keyboard

            final FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();  //FragmentTransaction to show the Fragment
            transaction.setCustomAnimations(R.animator.slide_in, 0);
            transaction.add(R.id.constraintLayoutCalculating, trueFragment);
            transaction.commitAllowingStateLoss();
            getSupportFragmentManager().executePendingTransactions();

            final Handler handler = new Handler();
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {

                    FragmentTransaction transaction2 = getSupportFragmentManager().beginTransaction();  //FragmentTransaction to remove the Fragment
                    transaction2.setCustomAnimations(0, R.animator.slide_out);
                    transaction2.remove(trueFragment);
                    transaction2.commitAllowingStateLoss();
                    editText.setText(null);  //reset EditText
                    editText.requestFocus();
                    textInputLayout.setError(null);
                    inputManager.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT);  //show Keyboard
                    fabNext.setEnabled(true);  //reenable button at the end - Problem: Button made clickable again, even if the remove FragmentTransaction animation hasn't finished

               }
         }, 1000);
}

This is the crash I am getting. A search on StackOverflow showed me that it has to do with starting a FragmentTransaction while another still isn't finished.

 --------- beginning of crash
2020-02-17 10:27:29.787 23467-23467/my.top.secretpreciousname E/AndroidRuntime: FATAL EXCEPTION: main
    my.top.secretpreciousname, PID: 23467
    java.lang.IllegalStateException: Restarter must be created only during owner's initialization stage
        at androidx.savedstate.SavedStateRegistryController.performRestore(SavedStateRegistryController.java:58)
        at androidx.fragment.app.Fragment.performCreate(Fragment.java:2585)
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:838)
        at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1238)
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1303)
        at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:439)
        at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManagerImpl.java:2079)
        at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManagerImpl.java:1869)
        at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:1824)
        at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManagerImpl.java:1727)
        at androidx.fragment.app.FragmentManagerImpl.executePendingTransactions(FragmentManagerImpl.java:183)
        at my.top.secretpreciousname.CalculatingActivity$1.onClick(CalculatingActivity.java:254)
        at android.view.View.performClick(View.java:7339)
        at my.top.secretpreciousname.CalculatingActivity$2.onEditorAction(CalculatingActivity.java:320)
        at android.widget.TextView.doKeyDown(TextView.java:8617)
        at android.widget.TextView.onKeyDown(TextView.java:8486)
        at android.view.KeyEvent.dispatch(KeyEvent.java:3359)
        at android.view.View.dispatchKeyEvent(View.java:13312)
        at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1912)
        at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1912)
        at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1912)
        at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1912)
        at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1912)
        at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1912)
        at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1912)
        at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1912)

Is there nice solution for this? Thanks in advance for any help.


Solution

  • It turns out that I have to add the line

    transaction2.addToBackStack(null);
    

    to the animated FragmentTransaction to achieve that it won't crash when a new Transaction is triggered before the previous one has finished its animation. Still not sure if this can be considered as a valid solution, as actually I don't want backstack functionality with these Fragments, so I have to temporarily block the back button or flush the Backstack after every Transaction.