A navigation library of View ui system which not supported in jetpack navigation.
If you want to use DSL to define a navigation but can't use Jetpack Compose, maybe you should consider this library. But If you need to write so many logic in a router, I recommend you to use Navigation for fragment which support DSL too.
The reason for this library is because the communication between fragments is too complicated, we can put state flows down and events flow up to build a unidirectional data flow which can make code more maintainable. ref: Architecting
ViewNavigator( /*...*/ ) {
navView(SCREEN_ONE) { /*...*/ }
navViewBinding(SCREEN_TWO) { /*...*/ }
navViewController(SCREEN_THREE) { /*...*/ }
}
- support DSL to build a navigation graph.
- support build View Router with View, ViewBinding and ViewController (something like light fragment).
- support args / stack restore when Activity recreate.
- support coroutine by using a job binding view lifecycle.
-
add jitpack to your root repositories.
dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { mavenCentral() maven { url 'https://jitpack.io' } } }
-
add dependencies.
dependencies { implementation 'com.github.FnGoose:ViewNavigation:Version' }
- create ViewNavigatorController.
val navController = ViewNavigatorController()
- create ViewNavigator with some screen.
ViewNavigator( navController = navController, initRoute = "ScreenOne", name = "MainNavigator" ) { navView("ScreenOne") { linearLayout(context) { addButton( text = "Navigate to Screen Two", onClick = { navController.navigateTo("ScreenTwo") } ) } } navView("ScreenTwo") { linearLayout(context) { addButton( text = "Pop", onClick = { navController.pop() } ) } } }
- optional, bind backpress to navController.
onBackPressedDispatcher.addCallback(this) { // Intercept back pressed event from MainActivity. navController.pop() }
Every router need a view. you can provide a View, no matter how you generate it. In this library, we support three way to generate a route.
- View
ViewNavigator() { navView("ScreenOne") { View(context) } }
- ViewBinding
ViewNavigator() { navViewBinding("ScreenOne") { ScreenOneBinding.inflate(layoutInflater) } }
- navViewController
If you use this way, you need to overwrite buildView function of ViewController to provide a view.
ViewNavigator() { navViewController("ScreenOne") { ScreenViewController() } }
In activity or fragment, we have onResume function to do something when the screen is foreground. In ViewNavigation, I use onAttachWindow and onDetachWindow to provide two function update and onDetach.
you can overwrite onPop to support nested Navigation.
class NestedController(
private var subNavController: ViewNavigatorController? = null
override fun onPop(view: View, targetRoute: String?): PopResult {
val subPopRet = subNavController.pop()
if (subPopRet) {
// current pop logc.
}
return super.onPop(view, targerRoute)
}
}
Translation of Navigation is very important.
ViewNavigation provide very flexible translation animation api.
fun interface NavViewAnimation {
/**
* On animate
*
* @param view the view you need to operate.
* @param progress from 0 to 1.
* @param width with of parent.
* @param height height of parent.
*/
fun onAnimate(
view: View,
progress: Float,
width: Int,
height: Int
)
}
And you can hign level api to create a interface:
inline fun alphaViewAnimation(
crossinline transform: (progress: Float) -> Float
) = NavViewAnimation { view, progress, _, _ -> view.alpha = transform(progress) }
inline fun horizontalViewAnimation(
crossinline transform: (progress: Float) -> Float
) = horizontalTranslationViewAnimation { progress, width, layoutDirection ->
width * layoutDirection.ldFlag * transform(progress)
}
inline fun horizontalTranslationViewAnimation(
crossinline transform: (progress: Float, width: Int, layoutDirection: Int) -> Float
) = NavViewAnimation { view, progress, width, _ ->
view.translationX = transform(progress, width, view.layoutDirection)
}
inline fun verticalViewAnimation(
crossinline transform: (progress: Float) -> Float
) = verticalTranslationViewAnimation { progress, height ->
height * transform(progress)
}
inline fun verticalTranslationViewAnimation(
crossinline transform: (progress: Float, height: Int) -> Float
) = NavViewAnimation { view, progress, _, height ->
view.translationY = transform(progress, height)
}
also you can comine two animation with +
NavViewAnimation.HorizontalEnterViewAnimation + NavViewAnimation.FadeEnterViewAnimation
I do support you to use official ViewGroup ViewNavigationContainerLayout, but you can define a custom Container if you want.
You need to implement ViewContainer to make your specific view container.
interface ViewContainer<V : View> {
/* ... */
}
You can also add some listener to listen something of ViewNavigation. such as route change, pop out.
viewNavigation.addRouteChangeListener { oldRoute: String, newRoute: String ->
Log.d(TAG, "oldRoute: $oldRoute, newRoute: $newRoute")
}
viewNavigator.addOnPopOutListener {
// finish activity or other...
finish()
}