android - How to avoid adding same fragment to stack

android - How to avoid adding same fragment to stack

Samar Michi Author: Samar Michi Date: 2022-10-05
android - How to avoid adding same fragment to stack

All you need to know about android - How to avoid adding same fragment to stack , in addintion to android fragment addToBackStack(null) :how to add the same fragment to stack just one time? , android - Trying avoid add same fragment twice to backStack, popBackStackImmediate always return false , android - How to determine fragment restored from backstack , android - How to listen to Fragment Backstack Pop only?

  1. android - How to avoid adding same fragment to stack
  2. Question:

    I need some help. em adding fragment to activity this way. problem is on each call of openFragment it create fragment and add. which is obvious. Question: what modification i do, so it can add fragment only once. on the next call with same fragment tag it will do nothing.

    case: press button first time it add fragment and shows. i press again same button it response nothing.

    public static void openFragment(Activity activity, Fragment fragment) {
    
        FragmentManager fragmentManager = ((ActionBarActivity) activity)
                .getSupportFragmentManager();
            FragmentTransaction ftx = fragmentManager.beginTransaction();
            ftx.addToBackStack(fragment.getClass().getSimpleName());
            ftx.setCustomAnimations(R.anim.slide_in_right,
                    R.anim.slide_out_left, R.anim.slide_in_left,
                    R.anim.slide_out_right);
            ftx.add(R.id.main_content, fragment);
            ftx.commit();
    }
    


    Solution 1:

    Here's the solution, It will only allow you to add fragment once in stack otherwise it will pop-out the very same fragment from stack.

    public static void openFragment(Activity activity, Fragment fragment) {
        String fragmentTag = fragment.getClass().getName();
        FragmentManager fragmentManager= ((ActionBarActivity) activity)
                .getSupportFragmentManager();
    
        boolean fragmentPopped = fragmentManager
                .popBackStackImmediate(fragmentTag , 0);
    
        if (!fragmentPopped && fragmentManager.findFragmentByTag(fragmentTag) == null) {
    
        FragmentTransaction ftx = fragmentManager.beginTransaction();
        ftx.addToBackStack(fragment.getClass().getSimpleName());
        ftx.setCustomAnimations(R.anim.slide_in_right,
                R.anim.slide_out_left, R.anim.slide_in_left,
                R.anim.slide_out_right);
        ftx.add(R.id.main_content, fragment);
        ftx.commit();
      }
    }
    

    slide_in_right

    <?xml version="1.0" encoding="utf-8"?>
      <set xmlns:android="http://schemas.android.com/apk/res/android"
       android:shareInterpolator="true">
      <translate android:fromXDelta="100%"
        android:toXDelta="0%" android:fromYDelta="0%"
        android:toYDelta="0%" android:duration="200">
      </translate>
    </set>
    

    slide_out_right

    <?xml version="1.0" encoding="utf-8"?>
       <set xmlns:android="http://schemas.android.com/apk/res/android"
        android:shareInterpolator="false">
        <translate android:fromXDelta="0%" android:toXDelta="100%"
        android:fromYDelta="0%" android:toYDelta="0%"
        android:duration="200">
      </translate>
    </set>
    

    slide_in_left

    <?xml version="1.0" encoding="utf-8"?>
      <set xmlns:android="http://schemas.android.com/apk/res/android"
      android:shareInterpolator="true">
      <translate android:fromXDelta="-100%"
        android:toXDelta="0%" android:fromYDelta="0%"
        android:toYDelta="0%" android:duration="200">
      </translate>
    </set>
    

    slide_out_left

    <?xml version="1.0" encoding="utf-8"?>
     <set xmlns:android="http://schemas.android.com/apk/res/android"
      android:shareInterpolator="true">
      <translate android:fromXDelta="0%"
        android:toXDelta="-100%" android:fromYDelta="0%"
        android:toYDelta="0%" android:duration="200">
      </translate>
    </set>
    

    And this is how you call this function:

    openFragment(activity, new MyFragment());
    

    Solution 2:

    Use FragmentTransaction.replace() instead of FragmentTransaction.add():

    This is essentially the same as calling remove(Fragment) for all currently added fragments that were added with the same containerViewId and then add(int, Fragment, String) with the same arguments given here.

    The first call to FragmentTransaction.replace() will simply add the fragment as there were no fragments to remove.

    Solution 3:

    check if fragment is already added or not by using this method:

    if(!Fragment.isAdded()){
    //add fragment
    }
    

  3. android fragment addToBackStack(null) :how to add the same fragment to stack just one time?
  4. Question:

    when fragment use addToBackStack(null) method and click a method many times in an activity, the fragment page will save to back stack everytime, when I press back key, it will restore the same page, how to add the same fragment to stack just one time?

        mSettingBtn.setOnClickListener(new OnClickListener() {
    
            @Override
            public void onClick(View v) {
                SettingFragment settingFragment = new SettingFragment();
    
                FragmentTransaction transaction = getFragmentManager().beginTransaction();
                transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
                transaction.replace(R.id.left_framelayout, settingFragment);
                transaction.addToBackStack(null);
                transaction.commit();
            }
        });
    


    Solution 1:

    When adding your Fragment to the backstack you should also pass a TAG to be able to identify that Fragment:

     .addToBackStack(SettingsFragment.TAG);
    

    Prior to adding the Fragment you will be able to check if this Fragment is allready in the backstack using :

     getFragmentMangager().findFragmentByTag(SettingsFragment.TAG);
    

    This will return null if the Fragment is not allready added.

  5. android - Trying avoid add same fragment twice to backStack, popBackStackImmediate always return false
  6. Question:

    I'm trying avoid adding the same fragment to backStack with this method:

    public static void replaceFragment(FragmentManager fragmentManager, Fragment fragment, Boolean addToBackStack) {
            String backStateName = fragment.getClass().getName();
            boolean fragmentPopped = fragmentManager.popBackStackImmediate(backStateName, 0);
    
            if (addToBackStack && !fragmentPopped && fragmentManager.findFragmentByTag(backStateName) == null) {
                fragmentManager
                        .beginTransaction()
    //                    .setCustomAnimations(android.R.anim.slide_in_left, android.R.anim.slide_out_right, android.R.anim.fade_in, android.R.anim.fade_out)
                        .replace(R.id.container, fragment)
                        .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
                        .addToBackStack(backStateName) // was 'backStateName'
                        .commit();
            } else {
                if (!addToBackStack)
                    fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
                fragmentManager
                        .beginTransaction()
    //                    .setCustomAnimations(android.R.anim.slide_in_left, android.R.anim.slide_out_right, android.R.anim.fade_in, android.R.anim.fade_out)
                        .replace(R.id.container, fragment)
                        .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
                        .disallowAddToBackStack()
                        .commit();
            }
        }
    

    Navigation Drawer contains: ImageView with profilePhoto which opens ProfileMainFragment and few categories which opens MainFragmentCategory. Boolean addToBackStack is false when fragment is choosen from navigationDrawerMenu and true when is choosen within fragment (move from MainFragmentCategory to DetialFragmentCategory) or click profilePhoto in navigationDrawer.

    fragmentPopped is always false, why is that so? Even if I click profilePhoto and again profilePhoto in navigationDrawer. It should avoid to add it to backStack for a second (and third, and fourth...) time, but it didn't.

    Any idea how can I make it right?


    Solution 1:

    You can add the following code before you replace your fragment.

    // Replace fragmentCotainer with your container id
    Fragment currentFragment = fragmentManager.findFragmentById(R.id.fragmentCotainer);
    // Return if the class are the same
    if(currentFragment.getClass().equals(fragment.getClass())) return;
    

  7. android - How to determine fragment restored from backstack
  8. Question:

    Been searching for this issue for a while to no avail now:

    How to determine fragment is being restored from backstack? I'm using the compatibility library and a ListFragment inside a FragmentActivity. When an item inside ListFragment is selected, a new Fragment is started to replace the ListFragment.

    I noticed that when the FragmentActivity gets paused, the Fragment's onSaveInstanceState is called. But when the Fragment is put into the back stack via FragmentTransaction, onSaveInstanceState doesn't get called, then the lifecycle methods onCreateView and onActivityCreated gets called with null savedInstanceState Bundle.

    I'm asking this because I want to load some data when the Fragment is created or restored, but not so when user comes back via. backstack.

    I've looked at How to check if Fragment was restored from a backstack? but want to add more details in hopes this would incite an answer.

    Edit: just noticed http://developer.android.com/reference/android/app/Fragment.html#onSaveInstanceState(android.os.Bundle) says

    Note however: this method may be called at any time before onDestroy(). There are many situations where a fragment may be mostly torn down (such as when placed on the back stack with no UI showing), but its state will not be saved until its owning activity actually needs to save its state.

    So onSaveInstanceState is definitely out of the question...


    Solution 1:

    I think that most simple way is do this for example in onViewCreated() method:

    if (savedInstanceState == null && !mAlreadyLoaded) {
        mAlreadyLoaded = true;
    
        // Do this code only first time, not after rotation or reuse fragment from backstack
    }
    

    Because when android put fragment on backstack, it only destroy its view, but don't kill instance itself, so mAlreadyLoaded will be still true when fragment will be restored from backstack.

    Solution 2:

    getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener() {    
                public void onBackStackChanged() {
                    Log.i(TAG, "back stack changed ");
                    int backCount = getSupportFragmentManager().getBackStackEntryCount();
                    if (backCount == 0){
                                       // block where back has been pressed. since backstack is zero.
                    }
                }
            });
    

    use this addOnBackStackChangedListener.

    Solution 3:

    When a fragment goes to back-stack onDestroyView() called. Not onDestroy().

    And when a fragment pops from back-stack onCreateView() called. Not onCreate().

    So add a boolean mIsRestoredFromBackstack to fragment and follow as below:

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        mIsRestoredFromBackstack = false;
    }
    
    @Override
    public void onResume()
    {
        super.onResume();
        if(mIsRestoredFromBackstack)
        {
            // The fragment restored from backstack, do some work here!
        }
    }
    
    @Override
    public void onDestroyView()
    {
        super.onDestroyView();
        mIsRestoredFromBackstack = true;
    }
    

  9. android - How to listen to Fragment Backstack Pop only?
  10. Question:

    I wanted to know when a Fragment has been pop from backstack.

    I have my code as below

    private void setupChildFragmentPopListener() {
        getChildFragmentManager().addOnBackStackChangedListener(
                new FragmentManager.OnBackStackChangedListener() {
                    @Override
                    public void onBackStackChanged() {
                        // Do whatever I want
                    }
                });
    }
    

    However, this code got triggered both when add and pop fragment. How could I have a way to listen only to pop fragment? Or a way to filter out in the onBackStackChanged() function if it is not pop?


    Solution 1:

    You can keep a count of the backstack entries and compare it everytime your onBackStackChanged() is called.

    private void setupChildFragmentPopListener() {
        getChildFragmentManager().addOnBackStackChangedListener(
                new FragmentManager.OnBackStackChangedListener() {
                    @Override
                    public void onBackStackChanged() {
                        int backCount = getChildFragmentManager().getBackStackEntryCount();
                    }
                });
    }
    

    Solution 2:

    For Kotlin lovers:

    supportFragmentManager.addOnBackStackChangedListener {
        if (supportFragmentManager.backStackEntryCount == 0) {
            fab.visibility = VISIBLE
        }
        // Other magic
    }