Add fragment to top backstask and remove old fragment năm 2024

In a project i came across a nice design which can only be implemented by using fragments . I think you would know about fragments , activities and handling their back stacks . Back stack of activities are easy to be handled by the way it is handled by android os. But whenever it comes to handling the back stack of a fragment with a complex design by provided by your designer we get little bit of worry . 🤔

OK let’s dive into it . You will find many blogs in which you will be taught how to handle back stack of a fragment with theory . I am sharing a real world design that how can actually implement it .

Designs

Main Feature arrives. Fragment back stack with designs.

Visualisation Link

OK let’s run into our basic fundamentals to achieve this.

Tracking Fragment History

This bunch of code can track the fragment history i.e. which fragment is add to back to back stack and which is removed. I am storing it in stack and if it is already added to back stack then it will not be added further.

import java.util.ArrayList;

public class FragmentHistory {

**private** ArrayList **stackArr**;
_/**_ 
* constructor to create stack with size * * @param
 _*/_ 
**public** FragmentHistory[] {  
    **stackArr** = **new** ArrayList[];
}
_/**_ 
* This method adds new entry to the top * of the stack * * @param entry
 _*_ **_@throws_** _Exception_ 
*/
**public void** push[**int** entry] {
    **if** [isAlreadyExists[entry]] {  
        **return**;  
    }  
    **stackArr**.add[entry];
}
**private boolean** isAlreadyExists[**int** entry] {  
    **return** [**stackArr**.contains[entry]];  
}
_/**_ 
* This method removes an entry from the * top of the stack. * * @return
 _*_ **_@throws_** _Exception_ 
*/
**public int** pop[] {
    **int** entry = -1;  
    **if**[!isEmpty[]]{
        entry = **stackArr**.get[**stackArr**.size[] - 1];
        **stackArr**.remove[**stackArr**.size[] - 1];  
    }  
    **return** entry;  
}
_/**_ 
* This method removes an entry from the * top of the stack. * * @return
 _*_ **_@throws_** _Exception_ 
*/
**public int** popPrevious[] {
    **int** entry = -1;
    **if**[!isEmpty[]]{  
        entry = **stackArr**.get[**stackArr**.size[] - 2];  
        **stackArr**.remove[**stackArr**.size[] - 2];  
    }  
    **return** entry;  
}
_/**_ 
* This method returns top of the stack * without removing it. * * @return
 _*/_ 
**public int** peek[] {  
    **if**[!isEmpty[]]{  
        **return stackArr**.get[**stackArr**.size[] - 1];  
    }
    **return** -1;  
}
**public boolean** isEmpty[]{  
    **return** [**stackArr**.size[] == 0];  
}
**public int** getStackSize[]{  
    **return stackArr**.size[];  
}
**public void** emptyStack[] {
    **stackArr**.clear[];  
}  
}

Fragment Transaction Class

This class is for transition style of the fragments .

import android.support.annotation.AnimRes; import android.support.annotation.StyleRes; import android.support.v4.app.FragmentTransaction; import android.util.Pair; import android.view.View;

import java.util.ArrayList; import java.util.List;

public class FragNavTransactionOptions {

List **sharedElements**;  
@FragNavController.Transit  
**int transition** = FragmentTransaction.**_TRANSITNONE_**;  
@AnimRes  
**int enterAnimation** = 0;  
@AnimRes  
**int exitAnimation** = 0;  
@AnimRes  
**int popEnterAnimation** = 0;  
@AnimRes  
**int popExitAnimation** = 0;  
@StyleRes  
**int transitionStyle** = 0;  
String **breadCrumbTitle**;  
String **breadCrumbShortTitle**;
**private** FragNavTransactionOptions[Builder builder] {  
    **sharedElements** = builder.**sharedElements**;  
    **transition** = builder.**transition**;  
    **enterAnimation** = builder.**enterAnimation**;  
    **exitAnimation** = builder.**exitAnimation**;  
    **transitionStyle** = builder.**transitionStyle**;  
    **popEnterAnimation** = builder.**popEnterAnimation**;  
    **popExitAnimation** = builder.**popExitAnimation**;  
    **breadCrumbTitle** = builder.**breadCrumbTitle**;  
    **breadCrumbShortTitle** = builder.**breadCrumbShortTitle**;  
}
**public static** Builder newBuilder[] {  
    **return new** Builder[];  
}
**public static final class** Builder {  
    **private** List **sharedElements**;  
    **private int transition**;  
    **private int enterAnimation**;  
    **private int exitAnimation**;  
    **private int transitionStyle**;  
    **private int popEnterAnimation**;  
    **private int popExitAnimation**;  
    **private** String **breadCrumbTitle**;  
    **private** String **breadCrumbShortTitle**;
    **private** Builder[] {  
    }
    **public** Builder addSharedElement[Pair val] {  
        **if** [**sharedElements** == **null**] {  
            **sharedElements** = **new** ArrayList[3];  
        }  
        **sharedElements**.add[val];  
        **return this**;  
    }
    **public** Builder sharedElements[List val] {  
        **sharedElements** = val;  
        **return this**;  
    }
    **public** Builder transition[@FragNavController.Transit **int** val] {  
        **transition** = val;  
        **return this**;  
    }
    **public** Builder customAnimations[@AnimRes **int** enterAnimation, @AnimRes **int** exitAnimation] {  
        **this**.**enterAnimation** = enterAnimation;  
        **this**.**exitAnimation** = exitAnimation;  
        **return this**;  
    }
    **public** Builder customAnimations[@AnimRes **int** enterAnimation, @AnimRes **int** exitAnimation, @AnimRes **int** popEnterAnimation, @AnimRes **int** popExitAnimation] {  
        **this**.**popEnterAnimation** = popEnterAnimation;  
        **this**.**popExitAnimation** = popExitAnimation;  
        **return** customAnimations[enterAnimation, exitAnimation];  
    }
    **public** Builder transitionStyle[@StyleRes **int** val] {  
        **transitionStyle** = val;  
        **return this**;  
    }
    **public** Builder breadCrumbTitle[String val] {  
        **breadCrumbTitle** = val;  
        **return this**;  
    }
    **public** Builder breadCrumbShortTitle[String val] {  
        **breadCrumbShortTitle** = val;  
        **return this**;  
    }
    **public** FragNavTransactionOptions build[] {  
        **return new** FragNavTransactionOptions[**this**];  
    }  
}  
}

Fragment Navigation Controller for Bottom Navigation Tab.

We have bottom navigation tabs [here is 5 tabs].For every tab we have fragments attached to it . So this class will control fragment transactions with respect to tabs .

import android.os.Bundle; import android.support.annotation.CheckResult; import android.support.annotation.IdRes; import android.support.annotation.IntDef; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.util.Pair; import android.view.View;

import org.json.JSONArray;

import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; import java.util.Stack;

@SuppressWarnings["RestrictedApi"] public class FragNavController {

**public static final int _NOTAB_** = -1;  
**public static final int _TAB1_** = 0;  
**public static final int _TAB2_** = 1;  
**public static final int _TAB3_** = 2;  
**public static final int _TAB4_** = 3;  
**public static final int _TAB5_** = 4;
**private static final int _MAXNUMTABS_** = 5;
_// Extras used to store savedInstanceState_ 
**private static final** String **_EXTRATAGCOUNT_** = FragNavController.**class**.getName[] + **":EXTRA_TAG_COUNT"**;  
**private static final** String **_EXTRASELECTEDTABINDEX_** = FragNavController.**class**.getName[] + **":EXTRA_SELECTED_TAB_INDEX"**;  
**private static final** String **_EXTRACURRENTFRAGMENT_** = FragNavController.**class**.getName[] + **":EXTRA_CURRENT_FRAGMENT"**;  
**private static final** String **_EXTRAFRAGMENTSTACK_** = FragNavController.**class**.getName[] + **":EXTRA_FRAGMENT_STACK"**;
@IdRes  
**private final int mContainerId**;  
@NonNull  
**private final** List **mFragmentStacks**;  
@NonNull  
**private final** FragmentManager **mFragmentManager**;  
**private final** FragNavTransactionOptions **mDefaultTransactionOptions**;  
@TabIndex  
**private int mSelectedTabIndex**;  
**private int mTagCount**;  
@Nullable  
**private** Fragment **mCurrentFrag**;  
@Nullable  
**private** DialogFragment **mCurrentDialogFrag**;  
@Nullable  
**private** RootFragmentListener **mRootFragmentListener**;  
@Nullable  
**private** TransactionListener **mTransactionListener**;  
**private boolean mExecutingTransaction**;
_//region Construction and setup_**private** FragNavController[Builder builder, @Nullable Bundle savedInstanceState] {  
    **mFragmentManager** = builder.**mFragmentManager**;  
    **mContainerId** = builder.**mContainerId**;  
    **mFragmentStacks** = **new** ArrayList[builder.**mNumberOfTabs**];  
    **mRootFragmentListener** = builder.**mRootFragmentListener**;  
    **mTransactionListener** = builder.**mTransactionListener**;  
    **mDefaultTransactionOptions** = builder.**mDefaultTransactionOptions**;  
    **mSelectedTabIndex** = builder.**mSelectedTabIndex**;
    _//Attempt to restore from bundle, if not, initialize_ 
    **if** [!restoreFromBundle[savedInstanceState, builder.**mRootFragments**]] {
        **for** [**int** i = 0; i < builder.**mNumberOfTabs**; i++] {  
            Stack stack = **new** Stack[];  
            **if** [builder.**mRootFragments** != **null**] {  
                stack.add[builder.**mRootFragments**.get[i]];  
            }  
            **mFragmentStacks**.add[stack];  
        }
        initialize[builder.**mSelectedTabIndex**];  
    }  
}
**public static** Builder newBuilder[@Nullable Bundle savedInstanceState, FragmentManager fragmentManager, **int** containerId] {  
    **return new** Builder[savedInstanceState, fragmentManager, containerId];  
}
_//endregion_
//region Transactions

/ * Function used to switch to the specified fragment stack * *

@param index The given index to switch to * @param transactionOptions Transaction options to be displayed * @throws IndexOutOfBoundsException Thrown if trying to switch to an index outside given range */

**public void** switchTab[@TabIndex **int** index, @Nullable FragNavTransactionOptions transactionOptions] **throws** IndexOutOfBoundsException {  
    _//Check to make sure the tab is within range_ 
    **if** [index >= **mFragmentStacks**.size[]] {  
        **throw new** IndexOutOfBoundsException[**"Can't switch to a tab that hasn't been initialized, "** +  
                **"Index : "** + index + **", current stack size : "** + **mFragmentStacks**.size[] +  
                **". Make sure to create all of the tabs you need in the Constructor or provide a way for them to be created via RootFragmentListener."**];  
    }  
    **if** [**mSelectedTabIndex** != index] {  
        **mSelectedTabIndex** = index;
        FragmentTransaction ft = createTransactionWithOptions[transactionOptions];
        detachCurrentFragment[ft];
        Fragment fragment = **null**;  
        **if** [index == **_NOTAB_**] {  
            ft.commit[];  
        } **else** {  
            _//Attempt to reattach previous fragment_ 
            fragment = reattachPreviousFragment[ft];  
            **if** [fragment != **null**] {  
                ft.commit[];  
            } **else** {  
                fragment = getRootFragment[**mSelectedTabIndex**];  
                ft.add[**mContainerId**, fragment, generateTag[fragment]];  
                ft.commit[];  
            }  
        }
        executePendingTransactions[];
        **mCurrentFrag** = fragment;  
        **if** [**mTransactionListener** != **null**] {  
            **mTransactionListener**.onTabTransaction[**mCurrentFrag**, **mSelectedTabIndex**];  
        }  
    }  
}
_/**_ 
* Function used to switch to the specified fragment stack * * @param index The given index to switch to * @throws IndexOutOfBoundsException Thrown if trying to switch to an index outside given range */
**public void** switchTab[@TabIndex **int** index] **throws** IndexOutOfBoundsException {  
    switchTab[index, **null**];  
}
_/**_ 
* Push a fragment onto the current stack * * @param fragment The fragment that is to be pushed * @param transactionOptions Transaction options to be displayed */
**public void** pushFragment[@Nullable Fragment fragment, @Nullable FragNavTransactionOptions transactionOptions] {  
    **if** [fragment != **null** && **mSelectedTabIndex** != **_NOTAB_**] {  
        FragmentTransaction ft = createTransactionWithOptions[transactionOptions];
        detachCurrentFragment[ft];  
        ft.add[**mContainerId**, fragment, generateTag[fragment]];  
        ft.commit[];
        executePendingTransactions[];
        **mFragmentStacks**.get[**mSelectedTabIndex**].push[fragment];
        **mCurrentFrag** = fragment;  
        **if** [**mTransactionListener** != **null**] {  
            **mTransactionListener**.onFragmentTransaction[**mCurrentFrag**, TransactionType.**_PUSH_**];  
        }
    }  
}
_/**_ 
* Push a fragment onto the current stack * * @param fragment The fragment that is to be pushed */
**public void** pushFragment[@Nullable Fragment fragment] {  
    pushFragment[fragment, **null**];  
}
_/**_ 
* Pop the current fragment from the current tab * * @param transactionOptions Transaction options to be displayed */
**public void** popFragment[@Nullable FragNavTransactionOptions transactionOptions] **throws** UnsupportedOperationException {  
    popFragments[1, transactionOptions];  
}
_/**_ 
* Pop the current fragment from the current tab */
**public void** popFragment[] **throws** UnsupportedOperationException {  
    popFragment[**null**];  
}
_/**_ 
* Pop the current stack until a given tag is found. If the tag is not found, the stack will popFragment until it is at * the root fragment * * @param transactionOptions Transaction options to be displayed */
**public void** popFragments[**int** popDepth, @Nullable FragNavTransactionOptions transactionOptions] **throws** UnsupportedOperationException {  
    **if** [isRootFragment[]] {  
        **throw new** UnsupportedOperationException[  
                **"You can not popFragment the rootFragment. If you need to change this fragment, use replaceFragment[fragment]"**];  
    } **else if** [popDepth < 1] {  
        **throw new** UnsupportedOperationException[**"popFragments parameter needs to be greater than 0"**];  
    } **else if** [**mSelectedTabIndex** == **_NOTAB_**] {  
        **throw new** UnsupportedOperationException[**"You can not pop fragments when no tab is selected"**];  
    }
    _//If our popDepth is big enough that it would just clear the stack, then call that._ 
    **if** [popDepth >= **mFragmentStacks**.get[**mSelectedTabIndex**].size[] - 1] {  
        clearStack[transactionOptions];  
        **return**;  
    }
    Fragment fragment;  
    FragmentTransaction ft = createTransactionWithOptions[transactionOptions];
    _//Pop the number of the fragments on the stack and remove them from the FragmentManager_ 
    **for** [**int** i = 0; i < popDepth; i++] {  
        fragment = **mFragmentManager**.findFragmentByTag[**mFragmentStacks**.get[**mSelectedTabIndex**].pop[].getTag[]];  
        **if** [fragment != **null**] {  
            ft.remove[fragment];  
        }  
    }
    _//Attempt to reattach previous fragment_ 
    fragment = reattachPreviousFragment[ft];
    **boolean** bShouldPush = **false**;  
    _//If we can't reattach, either pull from the stack, or create a new root fragment_ 
    **if** [fragment != **null**] {  
        ft.commit[];  
    } **else** {  
        **if** [!**mFragmentStacks**.get[**mSelectedTabIndex**].isEmpty[]] {  
            fragment = **mFragmentStacks**.get[**mSelectedTabIndex**].peek[];  
            ft.add[**mContainerId**, fragment, fragment.getTag[]];  
            ft.commit[];  
        } **else** {  
            fragment = getRootFragment[**mSelectedTabIndex**];  
            ft.add[**mContainerId**, fragment, generateTag[fragment]];  
            ft.commit[];
            bShouldPush = **true**;  
        }  
    }
    executePendingTransactions[];
    _//Need to have this down here so that that tag has been_ 
// committed to the fragment before we add to the stack
    **if** [bShouldPush] {  
        **mFragmentStacks**.get[**mSelectedTabIndex**].push[fragment];  
    }
    **mCurrentFrag** = fragment;  
    **if** [**mTransactionListener** != **null**] {  
        **mTransactionListener**.onFragmentTransaction[**mCurrentFrag**, TransactionType.**_POP_**];  
    }  
}
_/**_ 
* Pop the current fragment from the current tab */
**public void** popFragments[**int** popDepth] **throws** UnsupportedOperationException {  
    popFragments[popDepth, **null**];  
}
_/**_ 
* Clears the current tab's stack to get to just the bottom Fragment. This will reveal the root fragment * * @param transactionOptions Transaction options to be displayed */
**public void** clearStack[@Nullable FragNavTransactionOptions transactionOptions] {  
    **if** [**mSelectedTabIndex** == **_NOTAB_**] {  
        **return**;  
    }
    _//Grab Current stack_ 
    Stack fragmentStack = **mFragmentStacks**.get[**mSelectedTabIndex**];
    _// Only need to start popping and reattach if the stack is greater than 1_ 
    **if** [fragmentStack.size[] > 1] {  
        Fragment fragment;  
        FragmentTransaction ft = createTransactionWithOptions[transactionOptions];
        _//Pop all of the fragments on the stack and remove them from the FragmentManager_ 
        **while** [fragmentStack.size[] > 1] {  
            fragment = **mFragmentManager**.findFragmentByTag[fragmentStack.pop[].getTag[]];  
            **if** [fragment != **null**] {  
                ft.remove[fragment];  
            }  
        }
        _//Attempt to reattach previous fragment_ 
        fragment = reattachPreviousFragment[ft];
        **boolean** bShouldPush = **false**;  
        _//If we can't reattach, either pull from the stack, or create a new root fragment_ 
        **if** [fragment != **null**] {  
            ft.commit[];  
        } **else** {  
            **if** [!fragmentStack.isEmpty[]] {  
                fragment = fragmentStack.peek[];  
                ft.add[**mContainerId**, fragment, fragment.getTag[]];  
                ft.commit[];  
            } **else** {  
                fragment = getRootFragment[**mSelectedTabIndex**];  
                ft.add[**mContainerId**, fragment, generateTag[fragment]];  
                ft.commit[];
                bShouldPush = **true**;  
            }  
        }
        executePendingTransactions[];
        **if** [bShouldPush] {  
            **mFragmentStacks**.get[**mSelectedTabIndex**].push[fragment];  
        }
        _//Update the stored version we have in the list_ 
        **mFragmentStacks**.set[**mSelectedTabIndex**, fragmentStack];
        **mCurrentFrag** = fragment;  
        **if** [**mTransactionListener** != **null**] {  
            **mTransactionListener**.onFragmentTransaction[**mCurrentFrag**, TransactionType.**_POP_**];  
        }  
    }  
}
_/**_ 
* Clears the current tab's stack to get to just the bottom Fragment. This will reveal the root fragment. */
**public void** clearStack[] {  
    clearStack[**null**];  
}
_/**_ 
* Replace the current fragment * * @param fragment the fragment to be shown instead * @param transactionOptions Transaction options to be displayed */
**public void** replaceFragment[@NonNull Fragment fragment, @Nullable FragNavTransactionOptions transactionOptions] {  
    Fragment poppingFrag = getCurrentFrag[];
    **if** [poppingFrag != **null**] {  
        FragmentTransaction ft = createTransactionWithOptions[transactionOptions];
        _//overly cautious fragment popFragment_ 
        Stack fragmentStack = **mFragmentStacks**.get[**mSelectedTabIndex**];  
        **if** [!fragmentStack.isEmpty[]] {  
            fragmentStack.pop[];  
        }
        String tag = generateTag[fragment];  
        ft.replace[**mContainerId**, fragment, tag];
        _//Commit our transactions_ 
        ft.commit[];
        executePendingTransactions[];
        fragmentStack.push[fragment];  
        **mCurrentFrag** = fragment;
        **if** [**mTransactionListener** != **null**] {  
            **mTransactionListener**.onFragmentTransaction[**mCurrentFrag**, TransactionType.**_REPLACE_**];
        }  
    }  
}
_/**_ 
* Replace the current fragment * * @param fragment the fragment to be shown instead */
**public void** replaceFragment[@NonNull Fragment fragment] {  
    replaceFragment[fragment, **null**];  
}
_/**_ 
* @return Current DialogFragment being displayed. Null if none */
@Nullable  
@CheckResult  
**public** DialogFragment getCurrentDialogFrag[] {  
    **if** [**mCurrentDialogFrag** != **null**] {  
        **return mCurrentDialogFrag**;  
    }  
    _//Else try to find one in the FragmentManager_ 
    **else** {  
        FragmentManager fragmentManager;  
        **if** [**mCurrentFrag** != **null**] {  
            fragmentManager = **mCurrentFrag**.getChildFragmentManager[];  
        } **else** {  
            fragmentManager = **mFragmentManager**;  
        }  
        **if** [fragmentManager.getFragments[] != **null**] {  
            **for** [Fragment fragment : fragmentManager.getFragments[]] {  
                **if** [fragment **instanceof** DialogFragment] {  
                    **mCurrentDialogFrag** = [DialogFragment] fragment;  
                    **break**;  
                }  
            }  
        }  
    }  
    **return mCurrentDialogFrag**;  
}
_/**_ 
* Clear any DialogFragments that may be shown */
**public void** clearDialogFragment[] {  
    **if** [**mCurrentDialogFrag** != **null**] {  
        **mCurrentDialogFrag**.dismiss[];  
        **mCurrentDialogFrag** = **null**;  
    }  
    _// If we don't have the current dialog, try to find and dismiss it_ 
    **else** {  
        FragmentManager fragmentManager;  
        **if** [**mCurrentFrag** != **null**] {  
            fragmentManager = **mCurrentFrag**.getChildFragmentManager[];  
        } **else** {  
            fragmentManager = **mFragmentManager**;  
        }
        **if** [fragmentManager.getFragments[] != **null**] {  
            **for** [Fragment fragment : fragmentManager.getFragments[]] {  
                **if** [fragment **instanceof** DialogFragment] {  
                    [[DialogFragment] fragment].dismiss[];  
                }  
            }  
        }  
    }  
}
_/**_ 
* Display a DialogFragment on the screen * * @param dialogFragment The Fragment to be Displayed */
**public void** showDialogFragment[@Nullable DialogFragment dialogFragment] {  
    **if** [dialogFragment != **null**] {  
        FragmentManager fragmentManager;  
        **if** [**mCurrentFrag** != **null**] {  
            fragmentManager = **mCurrentFrag**.getChildFragmentManager[];  
        } **else** {  
            fragmentManager = **mFragmentManager**;  
        }
        _//Clear any current dialog fragments_ 
        **if** [fragmentManager.getFragments[] != **null**] {  
            **for** [Fragment fragment : fragmentManager.getFragments[]] {  
                **if** [fragment **instanceof** DialogFragment] {  
                    [[DialogFragment] fragment].dismiss[];  
                    **mCurrentDialogFrag** = **null**;  
                }  
            }  
        }
        **mCurrentDialogFrag** = dialogFragment;  
        **try** {  
            dialogFragment.show[fragmentManager, dialogFragment.getClass[].getName[]];  
        } **catch** [IllegalStateException e] {  
            _// Activity was likely destroyed before we had a chance to show, nothing can be done here._ 
        }  
    }  
}
_//endregion_
//region Private helper functions

/ * Helper function to make sure that we are starting with a clean slate and to perform our first fragment interaction. * *

@param index the tab index to initialize to */

**private void** initialize[@TabIndex **int** index] {  
    **mSelectedTabIndex** = index;  
    **if** [**mSelectedTabIndex** > **mFragmentStacks**.size[]] {  
        **throw new** IndexOutOfBoundsException[**"Starting index cannot be larger than the number of stacks"**];  
    }
    **mSelectedTabIndex** = index;  
    clearFragmentManager[];  
    clearDialogFragment[];
    **if** [index == **_NOTAB_**] {  
        **return**;  
    }
    FragmentTransaction ft = createTransactionWithOptions[**null**];
    Fragment fragment = getRootFragment[index];  
    ft.add[**mContainerId**, fragment, generateTag[fragment]];  
    ft.commit[];
    executePendingTransactions[];
    **mCurrentFrag** = fragment;  
    **if** [**mTransactionListener** != **null**] {  
        **mTransactionListener**.onTabTransaction[**mCurrentFrag**, **mSelectedTabIndex**];  
    }  
}
_/**_ 
* Helper function to get the root fragment for a given index. This is done by either passing them in the constructor, or dynamically via NavListener. * * @param index The tab index to get this fragment from * @return The root fragment at this index * @throws IllegalStateException This will be thrown if we can't find a rootFragment for this index. Either because you didn't provide it in the * constructor, or because your RootFragmentListener.getRootFragment[index] isn't returning a fragment for this index. */
@NonNull  
@CheckResult  
**private** Fragment getRootFragment[**int** index] **throws** IllegalStateException {  
    Fragment fragment = **null**;  
    **if** [!**mFragmentStacks**.get[index].isEmpty[]] {  
        fragment = **mFragmentStacks**.get[index].peek[];  
    } **else if** [**mRootFragmentListener** != **null**] {  
        fragment = **mRootFragmentListener**.getRootFragment[index];
        **if** [**mSelectedTabIndex** != **_NOTAB_**] {  
            **mFragmentStacks**.get[**mSelectedTabIndex**].push[fragment];  
        }
    }  
    **if** [fragment == **null**] {  
        **throw new** IllegalStateException[**"Either you haven't past in a fragment at this index in your constructor, or you haven't "** +  
                **"provided a way to create it while via your RootFragmentListener.getRootFragment[index]"**];  
    }
    **return** fragment;  
}
_/**_ 
* Will attempt to reattach a previous fragment in the FragmentManager, or return null if not able to. * * @param ft current fragment transaction * @return Fragment if we were able to find and reattach it */
@Nullable  
**private** Fragment reattachPreviousFragment[@NonNull FragmentTransaction ft] {  
    Stack fragmentStack = **mFragmentStacks**.get[**mSelectedTabIndex**];  
    Fragment fragment = **null**;  
    **if** [!fragmentStack.isEmpty[]] {  
        fragment = **mFragmentManager**.findFragmentByTag[fragmentStack.peek[].getTag[]];  
        **if** [fragment != **null**] {  
            ft.attach[fragment];  
        }  
    }  
    **return** fragment;  
}
_/**_ 
* Attempts to detach any current fragment if it exists, and if none is found, returns. * * @param ft the current transaction being performed */
**private void** detachCurrentFragment[@NonNull FragmentTransaction ft] {  
    Fragment oldFrag = getCurrentFrag[];  
    **if** [oldFrag != **null**] {  
        ft.detach[oldFrag];  
    }  
}
_/**_ 
* Helper function to attempt to get current fragment * * @return Fragment the current frag to be returned */
@Nullable  
@CheckResult  
**public** Fragment getCurrentFrag[] {  
    _//Attempt to used stored current fragment_ 
    **if** [**mCurrentFrag** != **null**] {  
        **return mCurrentFrag**;  
    } **else if** [**mSelectedTabIndex** == **_NOTAB_**] {  
        **return null**;  
    }  
    _//if not, try to pull it from the stack_ 
    **else** {  
        Stack fragmentStack = **mFragmentStacks**.get[**mSelectedTabIndex**];  
        **if** [!fragmentStack.isEmpty[]] {  
            **mCurrentFrag** = **mFragmentManager**.findFragmentByTag[**mFragmentStacks**.get[**mSelectedTabIndex**].peek[].getTag[]];  
        }  
    }  
    **return mCurrentFrag**;  
}
_/**_ 
* Create a unique fragment tag so that we can grab the fragment later from the FragmentManger * * @param fragment The fragment that we're creating a unique tag for * @return a unique tag using the fragment's class name */
@NonNull  
@CheckResult  
**private** String generateTag[@NonNull Fragment fragment] {  
    **return** fragment.getClass[].getName[] + ++**mTagCount**;  
}
_/**_ 
* This check is here to prevent recursive entries into executePendingTransactions */
**private void** executePendingTransactions[] {  
    **if** [!**mExecutingTransaction**] {  
        **mExecutingTransaction** = **true**;  
        **mFragmentManager**.executePendingTransactions[];  
        **mExecutingTransaction** = **false**;  
    }  
}
_/**_ 
* Private helper function to clear out the fragment manager on initialization. All fragment management should be done via FragNav. */
**private void** clearFragmentManager[] {  
    **if** [**mFragmentManager**.getFragments[] != **null**] {  
        FragmentTransaction ft = createTransactionWithOptions[**null**];  
        **for** [Fragment fragment : **mFragmentManager**.getFragments[]] {  
            **if** [fragment != **null**] {  
                ft.remove[fragment];  
            }  
        }  
        ft.commit[];  
        executePendingTransactions[];  
    }  
}
_/**_ 
* Setup a fragment transaction with the given option * * @param transactionOptions The options that will be set for this transaction */
@CheckResult  
**private** FragmentTransaction createTransactionWithOptions[@Nullable FragNavTransactionOptions transactionOptions] {  
    FragmentTransaction ft = **mFragmentManager**.beginTransaction[];  
    **if** [transactionOptions == **null**] {  
        transactionOptions = **mDefaultTransactionOptions**;  
    }  
    **if** [transactionOptions != **null**] {
        ft.setCustomAnimations[transactionOptions.**enterAnimation**, transactionOptions.**exitAnimation**, transactionOptions.**popEnterAnimation**, transactionOptions.**popExitAnimation**];  
        ft.setTransitionStyle[transactionOptions.**transitionStyle**];
        ft.setTransition[transactionOptions.**transition**];
        **if** [transactionOptions.**sharedElements** != **null**] {  
            **for** [Pair sharedElement : transactionOptions.**sharedElements**] {  
                ft.addSharedElement[sharedElement.**first**, sharedElement.**second**];  
            }  
        }
        **if** [transactionOptions.**breadCrumbTitle** != **null**] {  
            ft.setBreadCrumbTitle[transactionOptions.**breadCrumbTitle**];  
        }
        **if** [transactionOptions.**breadCrumbShortTitle** != **null**] {  
            ft.setBreadCrumbShortTitle[transactionOptions.**breadCrumbShortTitle**];
        }  
    }  
    **return** ft;  
}
_//endregion_
//region Public helper functions

/ * Get the number of fragment stacks * *

@return the number of fragment stacks */

@CheckResult  
**public int** getSize[] {  
    **return mFragmentStacks**.size[];  
}
_/**_ 
* Get a copy of the stack at a given index * * @return requested stack */
@SuppressWarnings[**"unchecked"**]  
@CheckResult  
@Nullable  
**public** Stack getStack[@TabIndex **int** index] {  
    **if** [index == **_NOTAB_**] **return null**;  
    **if** [index >= **mFragmentStacks**.size[]] {  
        **throw new** IndexOutOfBoundsException[**"Can't get an index that's larger than we've setup"**];  
    }  
    **return** [Stack] **mFragmentStacks**.get[index].clone[];  
}
_/**_ 
* Get a copy of the current stack that is being displayed * * @return Current stack */
@SuppressWarnings[**"unchecked"**]  
@CheckResult  
@Nullable  
**public** Stack getCurrentStack[] {  
    **return** getStack[**mSelectedTabIndex**];  
}
_/**_ 
* Get the index of the current stack that is being displayed * * @return Current stack index */
@CheckResult  
@TabIndex  
**public int** getCurrentStackIndex[] {  
    **return mSelectedTabIndex**;  
}
_/**_ 
* @return If true, you are at the bottom of the stack * [Consider using replaceFragment if you need to change the root fragment for some reason] * else you can popFragment as needed as your are not at the root */
@CheckResult  
**public boolean** isRootFragment[] {  
    Stack stack = getCurrentStack[];
    **return** stack == **null** || stack.size[] == 1;  
}
_//endregion_
//region SavedInstanceState

/ * Call this in your Activity's onSaveInstanceState[Bundle outState] method to save the instance's state. * *

@param outState The Bundle to save state information to */

**public void** onSaveInstanceState[@NonNull Bundle outState] {
    _// Write tag count_ 
    outState.putInt[**_EXTRATAGCOUNT_**, **mTagCount**];
    _// Write select tab_ 
    outState.putInt[**_EXTRASELECTEDTABINDEX_**, **mSelectedTabIndex**];
    _// Write current fragment_ 
    **if** [**mCurrentFrag** != **null**] {  
        outState.putString[**_EXTRACURRENTFRAGMENT_**, **mCurrentFrag**.getTag[]];  
    }
    _// Write stacks_ 
    **try** {  
        **final** JSONArray stackArrays = **new** JSONArray[];
        **for** [Stack stack : **mFragmentStacks**] {  
            **final** JSONArray stackArray = **new** JSONArray[];
            **for** [Fragment fragment : stack] {  
                stackArray.put[fragment.getTag[]];  
            }
            stackArrays.put[stackArray];  
        }
        outState.putString[**_EXTRAFRAGMENTSTACK_**, stackArrays.toString[]];  
    } **catch** [Throwable t] {  
        _// Nothing we can do_ 
    }  
}
_/**_ 
* Restores this instance to the state specified by the contents of savedInstanceState * * @param savedInstanceState The bundle to restore from * @param rootFragments List of root fragments from which to initialize empty stacks. If null, pull fragments from RootFragmentListener. * @return true if successful, false if not */
**private boolean** restoreFromBundle[@Nullable Bundle savedInstanceState, @Nullable List rootFragments] {  
    **if** [savedInstanceState == **null**] {  
        **return false**;  
    }
    _// Restore tag count_ 
    **mTagCount** = savedInstanceState.getInt[**_EXTRATAGCOUNT_**, 0];
    _// Restore current fragment_ 
    **mCurrentFrag** = **mFragmentManager**.findFragmentByTag[savedInstanceState.getString[**_EXTRACURRENTFRAGMENT_**]];
    _// Restore fragment stacks_ 
    **try** {  
        **final** JSONArray stackArrays = **new** JSONArray[savedInstanceState.getString[**_EXTRAFRAGMENTSTACK_**]];
        **for** [**int** x = 0; x < stackArrays.length[]; x++] {  
            **final** JSONArray stackArray = stackArrays.getJSONArray[x];  
            **final** Stack stack = **new** Stack[];
            **if** [stackArray.length[] == 1] {  
                **final** String tag = stackArray.getString[0];  
                **final** Fragment fragment;
                **if** [tag == **null** || **"null"**.equalsIgnoreCase[tag]] {  
                    **if** [rootFragments != **null**] {  
                        fragment = rootFragments.get[x];  
                    } **else** {  
                        fragment = getRootFragment[x];  
                    }
                } **else** {  
                    fragment = **mFragmentManager**.findFragmentByTag[tag];  
                }
                **if** [fragment != **null**] {  
                    stack.add[fragment];  
                }  
            } **else** {  
                **for** [**int** y = 0; y < stackArray.length[]; y++] {  
                    **final** String tag = stackArray.getString[y];
                    **if** [tag != **null** && !**"null"**.equalsIgnoreCase[tag]] {  
                        **final** Fragment fragment = **mFragmentManager**.findFragmentByTag[tag];
                        **if** [fragment != **null**] {  
                            stack.add[fragment];  
                        }  
                    }  
                }  
            }
            **mFragmentStacks**.add[stack];  
        }  
        _// Restore selected tab if we have one_ 
        **switch** [savedInstanceState.getInt[**_EXTRASELECTEDTABINDEX_**]] {  
            **case _TAB1_**:  
                switchTab[**_TAB1_**];  
                **break**;  
            **case _TAB2_**:  
                switchTab[**_TAB2_**];  
                **break**;  
            **case _TAB3_**:  
                switchTab[**_TAB3_**];  
                **break**;  
            **case _TAB4_**:  
                switchTab[**_TAB4_**];  
                **break**;  
            **case _TAB5_**:  
                switchTab[**_TAB5_**];  
                **break**;  
        }
        _//Successfully restored state_ 
        **return true**;  
    } **catch** [Throwable t] {  
        **return false**;  
    }  
}  
_//endregion_**public enum** TransactionType {  
    **_PUSH_**,  
    **_POP_**,  
    **_REPLACE_** 
}
_//Declare the TabIndex annotation_ 
@IntDef[{**_NOTAB_**, **_TAB1_**, **_TAB2_**, **_TAB3_**, **_TAB4_**,**_TAB5_**}]  
@Retention[RetentionPolicy.**_SOURCE_**]  
**public** @**interface** TabIndex {  
}
_// Declare Transit Styles_ 
@IntDef[{FragmentTransaction.**_TRANSITNONE_**, FragmentTransaction.**_TRANSITFRAGMENTOPEN_**, FragmentTransaction.**_TRANSITFRAGMENTCLOSE_**, FragmentTransaction.**_TRANSITFRAGMENTFADE_**}]  
@Retention[RetentionPolicy.**_SOURCE_**]  
@**interface** Transit {  
}
**public interface** RootFragmentListener {  
    _/**_ 
* Dynamically create the Fragment that will go on the bottom of the stack * * @param index the index that the root of the stack Fragment needs to go * @return the new Fragment */
    Fragment getRootFragment[**int** index];  
}
**public interface** TransactionListener {
    **void** onTabTransaction[Fragment fragment, **int** index];
    **void** onFragmentTransaction[Fragment fragment, TransactionType transactionType];  
}
**public static final class** Builder {  
    **private final int mContainerId**;  
    **private** FragmentManager **mFragmentManager**;  
    **private** RootFragmentListener **mRootFragmentListener**;  
    @TabIndex  
    **private int mSelectedTabIndex** = **_TAB1_**;  
    **private** TransactionListener **mTransactionListener**;  
    **private** FragNavTransactionOptions **mDefaultTransactionOptions**;  
    **private int mNumberOfTabs** = 0;  
    **private** List **mRootFragments**;  
    **private** Bundle **mSavedInstanceState**;
    **public** Builder[@Nullable Bundle savedInstanceState, FragmentManager mFragmentManager, **int** mContainerId] {  
        **this**.**mSavedInstanceState** = savedInstanceState;  
        **this**.**mFragmentManager** = mFragmentManager;  
        **this**.**mContainerId** = mContainerId;  
    }
    _/**_ 
* @param selectedTabIndex The initial tab index to be used must be in range of rootFragments size */
    **public** Builder selectedTabIndex[@TabIndex **int** selectedTabIndex] {  
        **mSelectedTabIndex** = selectedTabIndex;
        **if** [**mRootFragments** != **null** && **mSelectedTabIndex** > **mNumberOfTabs**] {  
            **throw new** IndexOutOfBoundsException[**"Starting index cannot be larger than the number of stacks"**];  
        }  
        **return this**;  
    }
    _/**_ 
* @param rootFragment A single root fragment. This library can still be helpful when managing a single stack of fragments */
    **public** Builder rootFragment[Fragment rootFragment] {  
        **mRootFragments** = **new** ArrayList[1];  
        **mRootFragments**.add[rootFragment];  
        **mNumberOfTabs** = 1;  
        **return** rootFragments[**mRootFragments**];  
    }
    _/**_ 
* @param rootFragments a list of root fragments. root Fragments are the root fragments that exist on any tab structure. If only one fragment is sent in, fragnav will still manage * transactions */
    **public** Builder rootFragments[@NonNull List rootFragments] {  
        **mRootFragments** = rootFragments;  
        **mNumberOfTabs** = rootFragments.size[];  
        **if** [**mNumberOfTabs** > **_MAXNUMTABS_**] {  
            **throw new** IllegalArgumentException[**"Number of root fragments cannot be greater than "** + **_MAXNUMTABS_**];  
        }  
        **return this**;  
    }
    _/**_ 
* @param transactionOptions The default transaction options to be used unless otherwise defined. */
    **public** Builder defaultTransactionOptions[@NonNull FragNavTransactionOptions transactionOptions] {  
        **mDefaultTransactionOptions** = transactionOptions;  
        **return this**;  
    }
    _/**_ 
* @param rootFragmentListener a listener that allows for dynamically creating root fragments * @param numberOfTabs the number of tabs that will be switched between */
    **public** Builder rootFragmentListener[RootFragmentListener rootFragmentListener, **int** numberOfTabs] {  
        **mRootFragmentListener** = rootFragmentListener;  
        **mNumberOfTabs** = numberOfTabs;  
        **if** [**mNumberOfTabs** > **_MAXNUMTABS_**] {  
            **throw new** IllegalArgumentException[**"Number of tabs cannot be greater than "** + **_MAXNUMTABS_**];  
        }  
        **return this**;  
    }
    _/**_ 
* @param val A listener to be implemented [typically within the main activity] to fragment transactions [including tab switches] */
    **public** Builder transactionListener[TransactionListener val] {  
        **mTransactionListener** = val;  
        **return this**;  
    }
    **public** FragNavController build[] {  
        **if** [**mRootFragmentListener** == **null** && **mRootFragments** == **null**] {  
            **throw new** IndexOutOfBoundsException[**"Either a root fragment[s] needs to be set, or a fragment listener"**];  
        }  
        **return new** FragNavController[**this**, **mSavedInstanceState**];  
    }
}  
}

Basic Fragment Class

This class has an interface called FragmentNavigation with method push fragment[]. This will push fragment to the stack in activity .

How do you remove fragments from backstack while navigating?

To remove destinations from the back stack when navigating from one destination to another, add a popUpTo[] argument to the associated navigate[] function call. popUpTo[] instructs the Navigation library to remove some destinations from the back stack as part of the call to navigate[] .

How do I add a fragment to backstack?

To place a fragment into the back stack, you need to explicitly call the addToBackStack[] method during a fragment transaction. //get the current display info Fragment1 fragment1 = new Fragment1[]; fragmentTransaction. replace[android.

How can I maintain fragment state when added to the back stack?

The logic to maintain its state is owned by the ViewModel . The ViewModel class inherently allows data to survive configuration changes, such as screen rotations, and remains in memory when the fragment is placed on the back stack.

How do I add a fragment to the top of my activity?

Add a fragment via XML To declaratively add a fragment to your activity layout's XML, use a FragmentContainerView element. The android:name attribute specifies the class name of the Fragment to instantiate.

Chủ Đề