Instantly share code, notes, and snippets.
Last active
August 11, 2020 23:14
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
-
Save GregKluska/4b91b8b44b6236c94c24fea8bb3d43eb to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import android.app.Activity | |
| import android.content.Context | |
| import androidx.annotation.IdRes | |
| import androidx.annotation.NavigationRes | |
| import androidx.fragment.app.Fragment | |
| import androidx.fragment.app.FragmentActivity | |
| import androidx.fragment.app.FragmentManager | |
| import androidx.navigation.NavController | |
| import androidx.navigation.findNavController | |
| import androidx.navigation.fragment.NavHostFragment | |
| import com.google.android.material.bottomnavigation.BottomNavigationView | |
| import com.gregkluska.restaurantmvvm.util.BottomNavController.OnNavigationReselectedListener | |
| /** | |
| * Class credit: Allan Veloso, Mitch Tabian | |
| * I took the concept from Allan Veloso and made alterations to fit our needs. | |
| * https://stackoverflow.com/questions/50577356/android-jetpack-navigation-bottomnavigationview-with-youtube-or-instagram-like#_=_ | |
| * @property navigationBackStack: Backstack for the bottom navigation | |
| */ | |
| class BottomNavController( | |
| val context: Context, | |
| @IdRes val containerId: Int, | |
| @IdRes val appStartDestinationId: Int, | |
| val graphChangeListener: OnNavigationGraphChanged?, | |
| val navGraphProvider: NavGraphProvider | |
| ) { | |
| private val TAG: String = "AppDebug" | |
| private val navigationBackStack = BackStack.of(appStartDestinationId) | |
| lateinit var activity: Activity | |
| lateinit var fragmentManager: FragmentManager | |
| lateinit var navItemChangeListener: OnNavigationItemChanged | |
| init { | |
| if (context is Activity) { | |
| activity = context | |
| fragmentManager = (activity as FragmentActivity).supportFragmentManager | |
| } | |
| } | |
| fun onNavigationItemSelected(itemId: Int = navigationBackStack.last()): Boolean { | |
| // Replace fragment representing a navigation item | |
| val fragment = fragmentManager.findFragmentByTag(itemId.toString()) | |
| ?: NavHostFragment.create(navGraphProvider.getNavGraphId(itemId)) | |
| fragmentManager.beginTransaction() | |
| .replace(containerId, fragment, itemId.toString()) | |
| .addToBackStack(null) | |
| .commit() | |
| // Add to back stack | |
| navigationBackStack.moveLast(itemId) | |
| // Update checked icon | |
| navItemChangeListener.onItemChanged(itemId) | |
| // communicate with Activity | |
| graphChangeListener?.onGraphChange() | |
| return true | |
| } | |
| fun onBackPressed() { | |
| val childFragmentManager = fragmentManager.findFragmentById(containerId)!! | |
| .childFragmentManager | |
| when { | |
| // We should always try to go back on the child fragment manager stack before going to | |
| // the navigation stack. It's important to use the child fragment manager instead of the | |
| // NavController because if the user change tabs super fast commit of the | |
| // supportFragmentManager may mess up with the NavController child fragment manager back | |
| // stack | |
| childFragmentManager.popBackStackImmediate() -> { | |
| } | |
| // Fragment back stack is empty so try to go back on the navigation stack | |
| navigationBackStack.size > 1 -> { | |
| // Remove last item from back stack | |
| navigationBackStack.removeLast() | |
| // Update the container with new fragment | |
| onNavigationItemSelected() | |
| } | |
| // If the stack has only one and it's not the navigation home we should | |
| // ensure that the application always leave from startDestination | |
| navigationBackStack.last() != appStartDestinationId -> { | |
| navigationBackStack.removeLast() | |
| navigationBackStack.add(0, appStartDestinationId) | |
| onNavigationItemSelected() | |
| } | |
| // Navigation stack is empty, so finish the activity | |
| else -> activity.finish() | |
| } | |
| } | |
| private class BackStack : ArrayList<Int>() { | |
| companion object { | |
| fun of(vararg elements: Int): BackStack { | |
| val b = BackStack() | |
| b.addAll(elements.toTypedArray()) | |
| return b | |
| } | |
| } | |
| fun removeLast() = removeAt(size - 1) | |
| fun moveLast(item: Int) { | |
| remove(item) // if present, remove | |
| add(item) // add to end of list | |
| } | |
| } | |
| // For setting the checked icon in the bottom nav | |
| interface OnNavigationItemChanged { | |
| fun onItemChanged(itemId: Int) | |
| } | |
| // Get id of each graph | |
| // ex: R.navigation.nav_blog | |
| // ex: R.navigation.nav_create_blog | |
| interface NavGraphProvider { | |
| @NavigationRes | |
| fun getNavGraphId(itemId: Int): Int | |
| } | |
| // Execute when Navigation Graph changes. | |
| // ex: Select a new item on the bottom navigation | |
| // ex: Home -> Account | |
| interface OnNavigationGraphChanged{ | |
| fun onGraphChange() | |
| } | |
| interface OnNavigationReselectedListener{ | |
| fun onReselectNavItem(navController: NavController, fragment: Fragment) | |
| } | |
| fun setOnItemNavigationChanged(listener: (itemId: Int) -> Unit) { | |
| this.navItemChangeListener = object : OnNavigationItemChanged { | |
| override fun onItemChanged(itemId: Int) { | |
| listener.invoke(itemId) | |
| } | |
| } | |
| } | |
| } | |
| // Convenience extension to set up the navigation | |
| fun BottomNavigationView.setUpNavigation( | |
| bottomNavController: BottomNavController, | |
| onReselectListener: OnNavigationReselectedListener | |
| ) { | |
| setOnNavigationItemSelectedListener { | |
| bottomNavController.onNavigationItemSelected(it.itemId) | |
| } | |
| setOnNavigationItemReselectedListener { | |
| bottomNavController | |
| .fragmentManager | |
| .findFragmentById(bottomNavController.containerId)!! | |
| .childFragmentManager | |
| .fragments[0]?.let { fragment -> | |
| onReselectListener.onReselectNavItem( | |
| bottomNavController.activity.findNavController(bottomNavController.containerId), | |
| fragment | |
| ) | |
| } | |
| } | |
| bottomNavController.setOnItemNavigationChanged { itemId -> | |
| menu.findItem(itemId).isChecked = true | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment