Skip to content

Instantly share code, notes, and snippets.

@farhanahmed95
Last active June 13, 2022 02:36
Show Gist options
  • Save farhanahmed95/a33aaabdb4fb602d515005c34e864ffe to your computer and use it in GitHub Desktop.
Save farhanahmed95/a33aaabdb4fb602d515005c34e864ffe to your computer and use it in GitHub Desktop.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:id="@+id/activityMain"
android:layout_height="match_parent">
<fragment
app:navGraph="@navigation/main_nav"
app:defaultNavHost="true"
android:id="@+id/navHostFragment"
android:name="com.example.MainHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentFactory;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.navigation.NavDestination;
import androidx.navigation.NavOptions;
import androidx.navigation.Navigator;
import androidx.navigation.fragment.FragmentNavigator;
import java.util.ArrayDeque;
import java.util.Map;
@Navigator.Name("fragment")
public class MainFragmentNavigator extends Navigator<FragmentNavigator.Destination> {
private static final String TAG = "FragmentNavigator";
private static final String KEY_BACK_STACK_IDS = "androidx-nav-fragment:navigator:backStackIds";
private final Context mContext;
private final FragmentManager mFragmentManager;
private final int mContainerId;
private ArrayDeque<Integer> mBackStack = new ArrayDeque<>();
public MainFragmentNavigator(@NonNull Context context, @NonNull FragmentManager manager,
int containerId) {
mContext = context;
mFragmentManager = manager;
mContainerId = containerId;
}
/**
* {@inheritDoc}
* <p>
* This method must call
* {@link FragmentTransaction#setPrimaryNavigationFragment(Fragment)}
* if the pop succeeded so that the newly visible Fragment can be retrieved with
* {@link FragmentManager#getPrimaryNavigationFragment()}.
* <p>
* Note that the default implementation pops the Fragment
* asynchronously, so the newly visible Fragment from the back stack
* is not instantly available after this call completes.
*/
@Override
public boolean popBackStack() {
if (mBackStack.isEmpty()) {
return false;
}
if (mFragmentManager.isStateSaved()) {
Log.i(TAG, "Ignoring popBackStack() call: FragmentManager has already"
+ " saved its state");
return false;
}
mFragmentManager.popBackStack(
generateBackStackName(mBackStack.size(), mBackStack.peekLast()),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
mBackStack.removeLast();
return true;
}
@NonNull
@Override
public FragmentNavigator.Destination createDestination() {
return new FragmentNavigator.Destination(this);
}
/**
* Instantiates the Fragment via the FragmentManager's
* {@link androidx.fragment.app.FragmentFactory}.
*
* Note that this method is <strong>not</strong> responsible for calling
* {@link Fragment#setArguments(Bundle)} on the returned Fragment instance.
*
* @param context Context providing the correct {@link ClassLoader}
* @param fragmentManager FragmentManager the Fragment will be added to
* @param className The Fragment to instantiate
* @param args The Fragment's arguments, if any
* @return A new fragment instance.
* @deprecated Set a custom {@link androidx.fragment.app.FragmentFactory} via
* {@link FragmentManager#setFragmentFactory(FragmentFactory)} to control
* instantiation of Fragments.
*/
@SuppressWarnings("DeprecatedIsStillUsed") // needed to maintain forward compatibility
@Deprecated
@NonNull
public Fragment instantiateFragment(@NonNull Context context,
@NonNull FragmentManager fragmentManager,
@NonNull String className, @SuppressWarnings("unused") @Nullable Bundle args) {
return fragmentManager.getFragmentFactory().instantiate(
context.getClassLoader(), className);
}
/**
* {@inheritDoc}
* <p>
* This method should always call
* {@link FragmentTransaction#setPrimaryNavigationFragment(Fragment)}
* so that the Fragment associated with the new destination can be retrieved with
* {@link FragmentManager#getPrimaryNavigationFragment()}.
* <p>
* Note that the default implementation commits the new Fragment
* asynchronously, so the new Fragment is not instantly available
* after this call completes.
*/
@SuppressWarnings("deprecation") /* Using instantiateFragment for forward compatibility */
@Nullable
@Override
public NavDestination navigate(@NonNull FragmentNavigator.Destination destination, @Nullable Bundle args,
@Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
if (mFragmentManager.isStateSaved()) {
Log.i(TAG, "Ignoring navigate() call: FragmentManager has already"
+ " saved its state");
return null;
}
String className = destination.getClassName();
if (className.charAt(0) == '.') {
className = mContext.getPackageName() + className;
}
final Fragment frag = instantiateFragment(mContext, mFragmentManager,
className, args);
frag.setArguments(args);
final FragmentTransaction ft = mFragmentManager.beginTransaction();
int enterAnim = navOptions != null ? navOptions.getEnterAnim() : -1;
int exitAnim = navOptions != null ? navOptions.getExitAnim() : -1;
int popEnterAnim = navOptions != null ? navOptions.getPopEnterAnim() : -1;
int popExitAnim = navOptions != null ? navOptions.getPopExitAnim() : -1;
if (enterAnim != -1 || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1) {
enterAnim = enterAnim != -1 ? enterAnim : 0;
exitAnim = exitAnim != -1 ? exitAnim : 0;
popEnterAnim = popEnterAnim != -1 ? popEnterAnim : 0;
popExitAnim = popExitAnim != -1 ? popExitAnim : 0;
ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim);
}
Fragment primaryNavigationFragment = mFragmentManager.getPrimaryNavigationFragment();
if (navOptions != null)
{
if (navOptions.getPopUpTo() != -1)
{
if (primaryNavigationFragment != null) {
ft.remove(primaryNavigationFragment);
}
}else {
if (primaryNavigationFragment != null) {
ft.hide(primaryNavigationFragment);
}
}
}
ft.add(mContainerId, frag);
ft.setPrimaryNavigationFragment(frag);
final @IdRes int destId = destination.getId();
final boolean initialNavigation = mBackStack.isEmpty();
// TODO Build first class singleTop behavior for fragments
final boolean isSingleTopReplacement = navOptions != null && !initialNavigation
&& navOptions.shouldLaunchSingleTop()
&& mBackStack.peekLast() == destId;
boolean isAdded;
if (initialNavigation) {
isAdded = true;
} else if (isSingleTopReplacement) {
// Single Top means we only want one instance on the back stack
if (mBackStack.size() > 1) {
// If the Fragment to be replaced is on the FragmentManager's
// back stack, a simple replace() isn't enough so we
// remove it from the back stack and put our replacement
// on the back stack in its place
mFragmentManager.popBackStack(
generateBackStackName(mBackStack.size(), mBackStack.peekLast()),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
ft.addToBackStack(generateBackStackName(mBackStack.size(), destId));
}
isAdded = false;
} else {
ft.addToBackStack(generateBackStackName(mBackStack.size() + 1, destId));
isAdded = true;
}
if (navigatorExtras instanceof FragmentNavigator.Extras) {
FragmentNavigator.Extras extras = (FragmentNavigator.Extras) navigatorExtras;
for (Map.Entry<View, String> sharedElement : extras.getSharedElements().entrySet()) {
ft.addSharedElement(sharedElement.getKey(), sharedElement.getValue());
}
}
ft.setReorderingAllowed(true);
ft.commit();
// The commit succeeded, update our view of the world
if (isAdded) {
mBackStack.add(destId);
return destination;
} else {
return null;
}
}
@Override
@Nullable
public Bundle onSaveState() {
Bundle b = new Bundle();
int[] backStack = new int[mBackStack.size()];
int index = 0;
for (Integer id : mBackStack) {
backStack[index++] = id;
}
b.putIntArray(KEY_BACK_STACK_IDS, backStack);
return b;
}
@Override
public void onRestoreState(@Nullable Bundle savedState) {
if (savedState != null) {
int[] backStack = savedState.getIntArray(KEY_BACK_STACK_IDS);
if (backStack != null) {
mBackStack.clear();
for (int destId : backStack) {
mBackStack.add(destId);
}
}
}
}
@NonNull
private String generateBackStackName(int backStackIndex, int destId) {
return backStackIndex + "-" + destId;
}
private int getDestId(@Nullable String backStackName) {
String[] split = backStackName != null ? backStackName.split("-") : new String[0];
if (split.length != 2) {
throw new IllegalStateException("Invalid back stack entry on the "
+ "NavHostFragment's back stack - use getChildFragmentManager() "
+ "if you need to do custom FragmentTransactions from within "
+ "Fragments created via your navigation graph.");
}
try {
// Just make sure the backStackIndex is correctly formatted
Integer.parseInt(split[0]);
return Integer.parseInt(split[1]);
} catch (NumberFormatException e) {
throw new IllegalStateException("Invalid back stack entry on the "
+ "NavHostFragment's back stack - use getChildFragmentManager() "
+ "if you need to do custom FragmentTransactions from within "
+ "Fragments created via your navigation graph.");
}
}
}
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.CallSuper;
import androidx.annotation.NavigationRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentContainerView;
import androidx.fragment.app.FragmentManager;
import androidx.navigation.NavController;
import androidx.navigation.NavGraph;
import androidx.navigation.NavHost;
import androidx.navigation.NavHostController;
import androidx.navigation.Navigation;
import androidx.navigation.Navigator;
import androidx.navigation.fragment.DialogFragmentNavigator;
import androidx.navigation.fragment.FragmentNavigator;
import androidx.navigation.fragment.NavHostFragment;
public class MainHostFragment extends Fragment implements NavHost {
private static final String KEY_GRAPH_ID = "android-support-nav:fragment:graphId";
private static final String KEY_START_DESTINATION_ARGS =
"android-support-nav:fragment:startDestinationArgs";
private static final String KEY_NAV_CONTROLLER_STATE =
"android-support-nav:fragment:navControllerState";
private static final String KEY_DEFAULT_NAV_HOST = "android-support-nav:fragment:defaultHost";
/**
* Find a {@link NavController} given a local {@link Fragment}.
*
* <p>This method will locate the {@link NavController} associated with this Fragment,
* looking first for a {@link NavHostFragment} along the given Fragment's parent chain.
* If a {@link NavController} is not found, this method will look for one along this
* Fragment's {@link Fragment#getView() view hierarchy} as specified by
* {@link Navigation#findNavController(View)}.</p>
*
* @param fragment the locally scoped Fragment for navigation
* @return the locally scoped {@link NavController} for navigating from this {@link Fragment}
* @throws IllegalStateException if the given Fragment does not correspond with a
* {@link NavHost} or is not within a NavHost.
*/
@NonNull
public static NavController findNavController(@NonNull Fragment fragment) {
Fragment findFragment = fragment;
while (findFragment != null) {
if (findFragment instanceof MainHostFragment) {
return ((MainHostFragment) findFragment).getNavController();
}
Fragment primaryNavFragment = findFragment.getParentFragmentManager()
.getPrimaryNavigationFragment();
if (primaryNavFragment instanceof MainHostFragment) {
return ((MainHostFragment) primaryNavFragment).getNavController();
}
findFragment = findFragment.getParentFragment();
}
// Try looking for one associated with the view instead, if applicable
View view = fragment.getView();
if (view != null) {
return Navigation.findNavController(view);
}
throw new IllegalStateException("Fragment " + fragment
+ " does not have a NavController set");
}
private NavHostController mNavController;
private Boolean mIsPrimaryBeforeOnCreate = null;
// State that will be saved and restored
private int mGraphId;
private boolean mDefaultNavHost;
/**
* Create a new NavHostFragment instance with an inflated {@link NavGraph} resource.
*
* @param graphResId resource id of the navigation graph to inflate
* @return a new NavHostFragment instance
*/
@NonNull
public static MainHostFragment create(@NavigationRes int graphResId) {
return create(graphResId, null);
}
/**
* Create a new NavHostFragment instance with an inflated {@link NavGraph} resource.
*
* @param graphResId resource id of the navigation graph to inflate
* @param startDestinationArgs arguments to send to the start destination of the graph
* @return a new NavHostFragment instance
*/
@NonNull
public static MainHostFragment create(@NavigationRes int graphResId,
@Nullable Bundle startDestinationArgs) {
Bundle b = null;
if (graphResId != 0) {
b = new Bundle();
b.putInt(KEY_GRAPH_ID, graphResId);
}
if (startDestinationArgs != null) {
if (b == null) {
b = new Bundle();
}
b.putBundle(KEY_START_DESTINATION_ARGS, startDestinationArgs);
}
final MainHostFragment result = new MainHostFragment();
if (b != null) {
result.setArguments(b);
}
return result;
}
/**
* Returns the {@link NavController navigation controller} for this navigation host.
* This method will return null until this host fragment's {@link #onCreate(Bundle)}
* has been called and it has had an opportunity to restore from a previous instance state.
*
* @return this host's navigation controller
* @throws IllegalStateException if called before {@link #onCreate(Bundle)}
*/
@NonNull
@Override
public final NavController getNavController() {
if (mNavController == null) {
throw new IllegalStateException("NavController is not available before onCreate()");
}
return mNavController;
}
@CallSuper
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
// TODO This feature should probably be a first-class feature of the Fragment system,
// but it can stay here until we can add the necessary attr resources to
// the fragment lib.
if (mDefaultNavHost) {
getParentFragmentManager().beginTransaction()
.setPrimaryNavigationFragment(this)
.commit();
}
}
@CallSuper
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Context context = requireContext();
mNavController = new NavHostController(context);
mNavController.setLifecycleOwner(this);
mNavController.setOnBackPressedDispatcher(requireActivity().getOnBackPressedDispatcher());
// Set the default state - this will be updated whenever
// onPrimaryNavigationFragmentChanged() is called
mNavController.enableOnBackPressed(
mIsPrimaryBeforeOnCreate != null && mIsPrimaryBeforeOnCreate);
mIsPrimaryBeforeOnCreate = null;
mNavController.setViewModelStore(getViewModelStore());
onCreateNavController(mNavController);
Bundle navState = null;
if (savedInstanceState != null) {
navState = savedInstanceState.getBundle(KEY_NAV_CONTROLLER_STATE);
if (savedInstanceState.getBoolean(KEY_DEFAULT_NAV_HOST, false)) {
mDefaultNavHost = true;
getParentFragmentManager().beginTransaction()
.setPrimaryNavigationFragment(this)
.commit();
}
mGraphId = savedInstanceState.getInt(KEY_GRAPH_ID);
}
if (navState != null) {
// Navigation controller state overrides arguments
mNavController.restoreState(navState);
}
if (mGraphId != 0) {
// Set from onInflate()
mNavController.setGraph(mGraphId);
} else {
// See if it was set by NavHostFragment.create()
final Bundle args = getArguments();
final int graphId = args != null ? args.getInt(KEY_GRAPH_ID) : 0;
final Bundle startDestinationArgs = args != null
? args.getBundle(KEY_START_DESTINATION_ARGS)
: null;
if (graphId != 0) {
mNavController.setGraph(graphId, startDestinationArgs);
}
}
}
/**
* Callback for when the {@link #getNavController() NavController} is created. If you
* support any custom destination types, their {@link Navigator} should be added here to
* ensure it is available before the navigation graph is inflated / set.
* <p>
* By default, this adds a {@link FragmentNavigator}.
* <p>
* This is only called once in {@link #onCreate(Bundle)} and should not be called directly by
* subclasses.
*
* @param navController The newly created {@link NavController}.
*/
@SuppressWarnings({"WeakerAccess", "deprecation"})
@CallSuper
protected void onCreateNavController(@NonNull NavController navController) {
navController.getNavigatorProvider().addNavigator(
new DialogFragmentNavigator(requireContext(), getChildFragmentManager()));
navController.getNavigatorProvider().addNavigator(createFragmentNavigator());
}
@CallSuper
@Override
public void onPrimaryNavigationFragmentChanged(boolean isPrimaryNavigationFragment) {
if (mNavController != null) {
mNavController.enableOnBackPressed(isPrimaryNavigationFragment);
} else {
mIsPrimaryBeforeOnCreate = isPrimaryNavigationFragment;
}
}
/**
* Create the FragmentNavigator that this NavHostFragment will use. By default, this uses
* {@link FragmentNavigator}, which replaces the entire contents of the NavHostFragment.
* <p>
* This is only called once in {@link #onCreate(Bundle)} and should not be called directly by
* subclasses.
* @return a new instance of a FragmentNavigator
* @deprecated Use {@link #onCreateNavController(NavController)}
*/
@SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated
@NonNull
protected Navigator<? extends FragmentNavigator.Destination> createFragmentNavigator() {
return new MainFragmentNavigator(requireContext(), getChildFragmentManager(),
getContainerId());
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
FragmentContainerView containerView = new FragmentContainerView(inflater.getContext());
// When added via XML, this has no effect (since this FragmentContainerView is given the ID
// automatically), but this ensures that the View exists as part of this Fragment's View
// hierarchy in cases where the NavHostFragment is added programmatically as is required
// for child fragment transactions
containerView.setId(getContainerId());
return containerView;
}
/**
* We specifically can't use {@link View#NO_ID} as the container ID (as we use
* {@link androidx.fragment.app.FragmentTransaction#add(int, Fragment)} under the hood),
* so we need to make sure we return a valid ID when asked for the container ID.
*
* @return a valid ID to be used to contain child fragments
*/
private int getContainerId() {
int id = getId();
if (id != 0 && id != View.NO_ID) {
return id;
}
// Fallback to using our own ID if this Fragment wasn't added via
// add(containerViewId, Fragment)
return R.id.nav_host_fragment_container;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (!(view instanceof ViewGroup)) {
throw new IllegalStateException("created host view " + view + " is not a ViewGroup");
}
Navigation.setViewNavController(view, mNavController);
// When added programmatically, we need to set the NavController on the parent - i.e.,
// the View that has the ID matching this NavHostFragment.
if (view.getParent() != null) {
View rootView = (View) view.getParent();
if (rootView.getId() == getId()) {
Navigation.setViewNavController(rootView, mNavController);
}
}
}
@CallSuper
@Override
public void onInflate(@NonNull Context context, @NonNull AttributeSet attrs,
@Nullable Bundle savedInstanceState) {
super.onInflate(context, attrs, savedInstanceState);
final TypedArray navHost = context.obtainStyledAttributes(attrs, R.styleable.NavHost);
final int graphId = navHost.getResourceId(R.styleable.NavHost_navGraph, 0);
if (graphId != 0) {
mGraphId = graphId;
}
navHost.recycle();
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NavHostFragment);
final boolean defaultHost = a.getBoolean(R.styleable.NavHostFragment_defaultNavHost, false);
if (defaultHost) {
mDefaultNavHost = true;
}
a.recycle();
}
@CallSuper
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
Bundle navState = mNavController.saveState();
if (navState != null) {
outState.putBundle(KEY_NAV_CONTROLLER_STATE, navState);
}
if (mDefaultNavHost) {
outState.putBoolean(KEY_DEFAULT_NAV_HOST, true);
}
if (mGraphId != 0) {
outState.putInt(KEY_GRAPH_ID, mGraphId);
}
}
}
@IuraGaitur
Copy link

Is it working for <dialog (Dialog fragments) tags ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment