Untitled

 avatar
unknown
plain_text
14 days ago
10 kB
4
Indexable
public class SavedBottomNavigationView extends BottomNavigationView {

    private int     firstFragmentGraphId = 0;
    private String  selectedItemTag;
    private String  firstFragmentTag;
    private boolean isOnFirstFragment;

    public SavedBottomNavigationView(@NonNull Context context) {
        super(context);
    }

    public SavedBottomNavigationView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public SavedBottomNavigationView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private void setupItemReselected(
            SparseArray<String> graphIdToTagMap,
            FragmentManager fragmentManager) {
        this.setOnNavigationItemReselectedListener(new OnNavigationItemReselectedListener() {
            @Override
            public void onNavigationItemReselected(@NonNull MenuItem item) {
                String newlySelectedItemTag = graphIdToTagMap.get(item.getItemId());
                NavHostFragment selectedFragment = (NavHostFragment) fragmentManager.findFragmentByTag(newlySelectedItemTag);
                NavController navController = selectedFragment.getNavController();
                navController.popBackStack(navController.getGraph().getStartDestination(), false);
            }
        });
    }

    public LiveData<NavController> setupWithNavController(
            List<Integer> navGraphIds,
            FragmentManager fragmentManager,
            int containerId, Intent intent) {
        final BottomNavigationView self = this;

        // Map of tags
        SparseArray<String> graphIdToTagMap = new SparseArray<String>();
        // Result. Mutable live data with the selected controlled
        MutableLiveData<NavController> selectedNavController = new MutableLiveData<NavController>();

        // First create a NavHostFragment for each NavGraph ID
        int index = 0;

        for (Integer navGraphId : navGraphIds) {
            String fragmentTag = getFragmentTag(index);

            // Find or create the Navigation host fragment
            NavHostFragment navHostFragment = obtainNavHostFragment(
                    fragmentManager,
                    fragmentTag,
                    navGraphId,
                    containerId);

            // Obtain its id
            int graphId = navHostFragment.getNavController().getGraph().getId();
            if (index == 0) {
                firstFragmentGraphId = graphId;
            }

            // Save to the map
            graphIdToTagMap.put(graphId, fragmentTag);

            // Attach or detach nav host fragment depending on whether it's the selected item.
            if (this.getSelectedItemId() == graphId) {
                // Update livedata with the selected graph
                selectedNavController.setValue(navHostFragment.getNavController());
                attachNavHostFragment(fragmentManager, navHostFragment, index == 0);
            } else {
                detachNavHostFragment(fragmentManager, navHostFragment);
            }

            index++;
        }

        // Now connect selecting an item with swapping Fragments
        selectedItemTag = graphIdToTagMap.get(this.getSelectedItemId());
        firstFragmentTag = graphIdToTagMap.get(firstFragmentGraphId);
        isOnFirstFragment = selectedItemTag.equals(firstFragmentTag);

        setOnNavigationItemSelectedListener(new OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                // Don't do anything if the state is state has already been saved.
                if (fragmentManager.isStateSaved()) {
                    return false;
                } else {
                    String newlySelectedItemTag = graphIdToTagMap.get(item.getItemId());
                    if (!selectedItemTag.equals(newlySelectedItemTag)) {
                        // Pop everything above the first fragment (the "fixed start destination")
                        fragmentManager.popBackStack(firstFragmentTag, FragmentManager.POP_BACK_STACK_INCLUSIVE);
                        NavHostFragment selectedFragment = (NavHostFragment) fragmentManager.findFragmentByTag(newlySelectedItemTag);
                        // Exclude the first fragment tag because it's always in the back stack.
                        if (!firstFragmentTag.equals(newlySelectedItemTag)) {
                            // Commit a transaction that cleans the back stack and adds the first fragment
                            // to it, creating the fixed started destination.
                            FragmentTransaction transaction = fragmentManager.beginTransaction()
                                    .show(selectedFragment)
                                    .setPrimaryNavigationFragment(selectedFragment);


                            for (int i = 0; i < graphIdToTagMap.size(); i++) {
                                int key = graphIdToTagMap.keyAt(i);
                                // get the object by the key.
                                String tag = graphIdToTagMap.get(key);
                                if (!tag.equals(newlySelectedItemTag)) {
                                    transaction.hide(fragmentManager.findFragmentByTag(firstFragmentTag));
                                }
                            }

                            transaction.addToBackStack(firstFragmentTag)
                                    .setReorderingAllowed(true)
                                    .commit();
                        }

                        selectedItemTag = newlySelectedItemTag;
                        isOnFirstFragment = selectedItemTag.equals(firstFragmentTag);
                        selectedNavController.setValue(selectedFragment.getNavController());
                        return true;
                    } else {
                        return false;
                    }
                }
            }
        });

        // Optional: on item reselected, pop back stack to the destination of the graph
        setupItemReselected(graphIdToTagMap, fragmentManager);

        // Handle deep link
        setupDeepLinks(navGraphIds, fragmentManager, containerId, intent);

        // Finally, ensure that we update our BottomNavigationView when the back stack changes
        fragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
            @Override
            public void onBackStackChanged() {
                if (!isOnFirstFragment && !isOnBackStack(fragmentManager, firstFragmentTag)) {
                    self.setSelectedItemId(firstFragmentGraphId);
                }

                // Reset the graph if the currentDestination is not valid (happens when the back
                // stack is popped after using the back button).
                NavController controller = selectedNavController.getValue();
                if (controller != null) {
                    if (controller.getCurrentDestination() == null) {
                        controller.navigate(controller.getGraph().getId());
                    }
                }
            }
        });
        return selectedNavController;
    }

    private void setupDeepLinks(
            List<Integer> navGraphIds,
            FragmentManager fragmentManager,
            int containerId,
            Intent intent
    ) {
        int index = 0;
        for (Integer navGraphId : navGraphIds) {
            String fragmentTag = getFragmentTag(index);

            // Find or create the Navigation host fragment
            NavHostFragment navHostFragment = obtainNavHostFragment(
                    fragmentManager, fragmentTag, navGraphId, containerId);

            // Handle Intent
            int graphId = navHostFragment.getNavController().getGraph().getId();
            if (navHostFragment.getNavController().handleDeepLink(intent)
                    && this.getSelectedItemId() != graphId) {
                this.setSelectedItemId(graphId);
            }

            index++;
        }
    }

    private void detachNavHostFragment(
            FragmentManager fragmentManager,
            NavHostFragment navHostFragment) {
        fragmentManager.beginTransaction()
                .hide(navHostFragment)
                .commitNow();
    }

    private void attachNavHostFragment(
            FragmentManager fragmentManager,
            NavHostFragment navHostFragment,
            boolean isPrimaryNavFragment) {
        FragmentTransaction transaction = fragmentManager.beginTransaction()
                .show(navHostFragment);

        if (isPrimaryNavFragment) {
            transaction.setPrimaryNavigationFragment(navHostFragment);
        }

        transaction.commitNow();
    }

    private NavHostFragment obtainNavHostFragment(
            FragmentManager fragmentManager,
            String fragmentTag,
            int navGraphId,
            int containerId) {
        // If the Nav Host fragment exists, return it
        Fragment fragment = fragmentManager.findFragmentByTag(fragmentTag);
        if (fragment != null && fragment instanceof NavHostFragment) {
            return (NavHostFragment) fragment;
        }

        // Otherwise, create it and return it.
        NavHostFragment navHostFragment = NavHostFragment.create(navGraphId);
        fragmentManager.beginTransaction()
                .add(containerId, navHostFragment, fragmentTag)
                .commitNow();
        return navHostFragment;
    }


    private boolean isOnBackStack(FragmentManager fragmentManager, String backStackName) {
        int backStackCount = fragmentManager.getBackStackEntryCount();
        for (int i = 0; i < backStackCount; i++) {
            FragmentManager.BackStackEntry entry = fragmentManager.getBackStackEntryAt(i);
            if (entry.getName().equals(backStackName)) {
                return true;
            }
        }
        return false;
    }

    public String getFragmentTag(int index) {
        return "bottomNavigation#" + index;
    }
}
Leave a Comment