Instantly share code, notes, and snippets.
Last active
March 14, 2023 03:10
-
Star
1
(1)
You must be signed in to star a gist -
Fork
1
(1)
You must be signed in to fork a gist
-
Save samuel22gj/aa18f5bdb63b93087b2f41407460a81a 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
/** | |
* A mediator to link a fragment container with a BottomNavigationView. | |
* | |
* The mediator will synchronize the fragment container's content | |
* with the selected item when a item of BottomNavigationView is selected. | |
*/ | |
class FragmentBottomNavigationMediator( | |
private val fragmentManager: FragmentManager, | |
@IdRes private val containerViewId: Int, | |
private val bottomNavigationView: BottomNavigationView, | |
private val accountDelegate: AccountDelegate, | |
private val i13nDelegate: I13nDelegate | |
) { | |
private val itemMap: MutableMap<Int, Item> = mutableMapOf() | |
var currentFragment: Fragment? = null | |
private set | |
fun addItems(vararg items: Item) { | |
items.forEach { itemMap[it.menuId] = it } | |
} | |
fun attach() { | |
bottomNavigationView.setOnNavigationItemSelectedListener { menuItem: MenuItem -> | |
val item = itemMap[menuItem.itemId] | |
item ?: return@setOnNavigationItemSelectedListener false | |
if (item.isLoginRequired && !accountDelegate.isLogin()) { | |
accountDelegate.requestLogin { bottomNavigationView.selectedItemId = item.menuId } | |
return@setOnNavigationItemSelectedListener false | |
} | |
fragmentManager.commit(allowStateLoss = true) { | |
currentFragment?.let { hide(it) } | |
val fragmentInStack = fragmentManager.findFragmentByTag(item.fragmentTag) | |
if (fragmentInStack is Fragment) { | |
currentFragment = fragmentInStack | |
show(fragmentInStack) | |
} else { | |
val fragment = item.fragmentInitializer.invoke() | |
currentFragment = fragment | |
add(containerViewId, fragment, item.fragmentTag) | |
} | |
} | |
i13nDelegate.logItemSelected(menuItem.itemId) | |
return@setOnNavigationItemSelectedListener true | |
} | |
bottomNavigationView.setOnNavigationItemReselectedListener { menuItem: MenuItem -> | |
val item = itemMap[menuItem.itemId] | |
item ?: return@setOnNavigationItemReselectedListener | |
if (currentFragment == null) { | |
fragmentManager.commit(allowStateLoss = true) { | |
val fragment = item.fragmentInitializer.invoke() | |
currentFragment = fragment | |
add(containerViewId, fragment, item.fragmentTag) | |
} | |
return@setOnNavigationItemReselectedListener | |
} | |
// If there are child fragments in the stack, pop all of them out, otherwise scroll itself to top. | |
currentFragment?.run { | |
if (childFragmentManager.backStackEntryCount > 0) { | |
do { | |
childFragmentManager.popBackStackImmediate() | |
} while (childFragmentManager.backStackEntryCount > 0) | |
} else { | |
scrollToTop() | |
} | |
} | |
i13nDelegate.logItemSelected(menuItem.itemId) | |
} | |
} | |
fun detach() { | |
bottomNavigationView.setOnNavigationItemSelectedListener(null) | |
bottomNavigationView.setOnNavigationItemReselectedListener(null) | |
itemMap.clear() | |
currentFragment = null | |
fragmentManager.commit(allowStateLoss = true) { | |
fragmentManager.fragments.forEach { remove(it) } | |
} | |
} | |
fun onLogout() { | |
val item = itemMap[bottomNavigationView.selectedItemId] | |
item ?: return | |
// If the current tab is login-required, switch to first non-login-required tab. | |
if (item.isLoginRequired) { | |
val firstNonLoginRequiredItem = itemMap.values.asSequence() | |
.firstOrNull { !it.isLoginRequired && fragmentManager.findFragmentByTag(it.fragmentTag) != null } | |
if (firstNonLoginRequiredItem != null) { | |
bottomNavigationView.selectedItemId = firstNonLoginRequiredItem.menuId | |
} | |
} | |
// Remove all login-required tab in container | |
fragmentManager.commit(allowStateLoss = true) { | |
itemMap.values.asSequence() | |
.filter { it.isLoginRequired } | |
.map { fragmentManager.findFragmentByTag(it.fragmentTag) } | |
.filterNotNull() | |
.forEach { remove(it) } | |
} | |
} | |
class Item( | |
val menuId: Int, | |
val fragmentTag: String, | |
val isLoginRequired: Boolean, | |
val eventName: String, | |
val fragmentInitializer: () -> Fragment | |
) { | |
override fun equals(other: Any?): Boolean { | |
if (this === other) return true | |
if (other === null || javaClass != other.javaClass) return false | |
val item = other as Item | |
return Objects.equals(menuId, item.menuId) | |
} | |
override fun hashCode(): Int { | |
return Objects.hash(menuId) | |
} | |
} | |
interface AccountDelegate { | |
fun isLogin(): Boolean | |
fun requestLogin(onSuccess: () -> Unit) | |
} | |
interface I13nDelegate { | |
fun logItemSelected(@IdRes menuId: Int) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment