Skip to content

Add fragments (not replacing) with animation problem

When I used fragment (.add) method and used “fade_in/fade_out” animation When moving between them. It is gives me this error “java.lang.IllegalStateException: commit already called”. I followed this tutorial to add fragments and this question to use animation.

Her’s my code

final Fragment fragment1 = new HomeFragment();
final Fragment fragment2 = new LibraryFragment();
final Fragment fragment3 = new ImageFragment();
final FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
Fragment active = fragment1;

private void initializeBottomNavigation() {
    // animations
    ft.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
    ft.setCustomAnimations(android.R.anim.fade_out, android.R.anim.fade_in);

    // add fragments
    ft.add(R.id.fragment_layout, fragment3, "3").hide(fragment3).commit();
    ft.add(R.id.fragment_layout, fragment2, "2").hide(fragment2).commit();
    ft.add(R.id.fragment_layout,fragment1, "1").commit();

    // show and hid them when click on BottomNav items
    BottomNavigationView navigationView = findViewById(R.id.bottom_navigation);
    navigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) {
            switch (item.getItemId()) {
                case R.id.home_item:
                    ft.hide(active).show(fragment1).commit();
                    active = fragment1;
                    return true;

                case R.id.books_item:
                    ft.hide(active).show(fragment2).commit();
                    active = fragment2;
                    return true;

                case R.id.image_item:
                    ft.hide(active).show(fragment3).commit();
                    active = fragment3;
                    return true;
            }
            return false;
        }
    });
}

Answer

FragmentTransaction is a one-time use object. You shouldn’t reuse it in multiple actions, but rather start a new transaction every time you want to change some Fragments.

In your case this is: once during initialisation, and once on every bottom bar press.

First of all, remove the line FragmentTransition ft = fm.beginTransaction() from the top since you can’t reuse that object.

Then change the function like so where you create a new transaction for every button press:

private void initializeBottomNavigation() {
    // first one transaction to add each Fragment
    FragmentTransaction ft = fm.beginTransaction();
    ft.add(R.id.fragment_layout, fragment3, "3").hide(fragment3);
    ft.add(R.id.fragment_layout, fragment2, "2").hide(fragment2);
    ft.add(R.id.fragment_layout, fragment1, "1");
    // commit once! to finish the transaction
    ft.commit();

    // show and hide them when click on BottomNav items
    BottomNavigationView navigationView = findViewById(R.id.bottom_navigation);
    navigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) {
            // start a new transaction
            FragmentTransaction ft = fm.beginTransaction();
            // animations
            ft.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
            ft.setCustomAnimations(android.R.anim.fade_out, android.R.anim.fade_in);
            switch (item.getItemId()) {
                case R.id.home_item:
                    ft.hide(active).show(fragment1).commit();
                    active = fragment1;
                    return true;

                case R.id.books_item:
                    ft.hide(active).show(fragment2).commit();
                    active = fragment2;
                    return true;

                case R.id.image_item:
                    ft.hide(active).show(fragment3).commit();
                    active = fragment3;
                    return true;
            }
            return false;
        }
    });
}