Skip to content

Instantly share code, notes, and snippets.

@TomohikoSato
Last active April 4, 2019 08:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TomohikoSato/0c80441743d5aaddbdb3613cfdc3e316 to your computer and use it in GitHub Desktop.
Save TomohikoSato/0c80441743d5aaddbdb3613cfdc3e316 to your computer and use it in GitHub Desktop.
画面遷移用DSL
class HogeFragment: Fragment, Screen {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
hogeButton.setOnClickListener { navigate(Event.HogeOkClicked()) }
}
}
class SampleActivity : AppCompatActivity(), Screen {
private val route: Route by Extra()
private val navigator by lazy { createNavigator(route, supportFragmentManager, R.id.fragment_nav_container, this) }
override fun onCreate(savedInstanceState: Bundle?) {
/** Activity初期生成の場合のみ[navigate]によって表示するFragmentを指定する。Activity再生成の場合は、Activity生成前に表示していたFragmentがAndroidOSによって再表示されれば良いので、何もしない。 */
if (savedInstanceState == null) navigate(this, Event.OnRootCreated)
}
}
interface Screen
sealed class Event {
object HogeOkClicked : Event()
object FugaOkClicked : Event()
object FugaCancelClicked : Event()
}
fun createNavigator(fm: FragmentManager, @IdRes container: Int, activity: Activity): ScreenNavigator<Screen, Event> =
navigator {
screen<SampleActivity> {
on<OnRootCreated> { /** 遷移処理 */ }
}
screen<HogeFragment> {
on<HogeOkClicked> { /** 遷移処理 */ }
}
screen<FugaFragment> {
on<FugaOkClicked> { /** 遷移処理 */ }
on<FugaCancelClicked> { /** 遷移処理 */ }
}
}
}
data class ScreenNavigator<SCREEN, EVENT>(
private val screenDefinitions: Map<Class<out SCREEN>, ScreenDefinition<SCREEN, EVENT>>) {
/**
* [navigator]で登録した中から、[fromScreen]と[event]に対応する遷移関数を探し実行する
* @throws NavigationNotFoundException
*/
fun navigate(fromScreen: SCREEN, event: EVENT): SCREEN =
findScreenDefinition(fromScreen)?.findEventDefinition(event)?.navigate(fromScreen, event)
?: throw NavigationNotFoundException("fromScreen:$fromScreen, event:$event")
private fun findScreenDefinition(screen: SCREEN): ScreenDefinition<SCREEN, EVENT>? =
screenDefinitions
.filterKeys { screenClass -> screenClass.isInstance(screen) }
.values
.firstOrNull()
data class ScreenDefinition<SCREEN, EVENT>(private val eventDefinitions: Map<Class<out EVENT>, EventDefinition<SCREEN, EVENT>>) {
fun findEventDefinition(event: EVENT): EventDefinition<SCREEN, EVENT>? =
eventDefinitions
.filterKeys { eventClass -> eventClass.isInstance(event) }
.values
.firstOrNull()
data class EventDefinition<SCREEN, EVENT>(val navigateFunc: (SCREEN, EVENT) -> SCREEN) {
fun navigate(fromScreen: SCREEN, event: EVENT): SCREEN = navigateFunc(fromScreen, event)
}
}
class Builder<SCREEN, EVENT> {
val screenDefinitions = mutableMapOf<Class<out SCREEN>, ScreenDefinition<SCREEN, EVENT>>()
inline fun <reified S : SCREEN> screen(noinline init: ScreenDefinitionBuilder<S>.() -> Unit) {
require(!screenDefinitions.containsKey(S::class.java)) { "${S::class.java.simpleName}は既に定義されています" }
screenDefinitions[S::class.java] = ScreenDefinitionBuilder<S>().apply(init).build()
}
fun build(): ScreenNavigator<SCREEN, EVENT> = ScreenNavigator(screenDefinitions.toMap())
inner class ScreenDefinitionBuilder<S : SCREEN> {
val eventDefinitions = mutableMapOf<Class<out EVENT>, ScreenDefinition.EventDefinition<SCREEN, EVENT>>()
inline fun <reified E : EVENT> on(noinline navigateFunc: S.(E) -> SCREEN) {
require(!eventDefinitions.containsKey(E::class.java)) { "${E::class.java.simpleName}は既に定義されています" }
@Suppress("UNCHECKED_CAST")
eventDefinitions[E::class.java] = ScreenDefinition.EventDefinition { fromScreen, event -> navigateFunc((fromScreen as S), event as E) }
}
fun build() = ScreenDefinition(eventDefinitions)
}
}
companion object {
/** [SCREEN]と[EVENT]、それらに対応する遷移関数を登録していく */
fun <SCREEN, EVENT> navigator(init: Builder<SCREEN, EVENT>.() -> Unit): ScreenNavigator<SCREEN, EVENT> =
Builder<SCREEN, EVENT>().apply(init).build()
}
}
class NavigationNotFoundException(msg: String) : Exception(msg)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment