In a pure world, Scenes are completely platform agnostic.
This means that there is absolutely no reference to any of the classes in the
Android SDK.
Unfortunately, this decoupling also means some mapping needs to exist between
different Scene implementations and the layouts that should represent these
Scenes.
Otherwise, how would the Activity know what layout to inflate for the user?
The ViewControllerFactory
The ViewControllerFactory is the interface that represents this
mapping.
It provides a viewControllerFor(Scene, ViewGroup): ViewController
method that
inflates the right layout and results a proper ViewController
instance for the Scene.
The supports(Scene): Boolean
method in that same interface allows us to
compose several ViewControllerFactories together.
1 interface ViewControllerFactory {
2
3 fun supports(scene: Scene<*>): Boolean
4 fun viewControllerFor(scene: Scene<*>, parent: ViewGroup): ViewController
5 }
A ViewControllerFactory for a single Scene MyScene
could for example look like
this:
1 class MySceneViewControllerFactory : ViewControllerFactory {
2
3 override fun supports(scene: Scene<*>) = scene is MyScene
4
5 override fun viewControllerFor(scene: Scene<*>, parent: ViewGroup) : ViewController {
6 return MySceneViewController(parent.inflate(R.layout.my_scene)
7 }
8 }
ProvidesView
If we allow ourselves to be flexible with the 'platform agnostic' requirement,
we can store this layout
The ext-acorn-android
artifact provides a special ProvidesView
interface that extends ViewControllerFactory and allows this:
1 class MyScene<MyContainer> : Scene, ProvidesView {
2
3 override fun createViewController(parent: ViewGroup): ViewController {
4 return MyViewController(parent.inflate(R.layout.myscene))
5 }
6 }
The SceneViewControllerFactory implementation can be used to deal with Scenes implementing this interface, saving us from having to implement a ViewControllerFactory.
The ViewControllerFactory DSL
To save you from having to implement the ViewControllerFactory each time, the bindViews method allows you to quickly create the Scene-layout mapping in a DSL-like manner:
1 val myViewControllerFactory = bindViews {
2
3 bind(
4 sceneKey = defaultKey<FirstScene>(),
5 layoutResId = R.layout.first_scene,
6 wrapper = { view: View ->
7 FirstSceneViewController(view)
8 }
9 )
10
11 // Or, shorter
12 bind(
13 defaultKey<SecondScene>(),
14 R.layout.second_scene,
15 ::SecondSceneViewController
16 )
17 }
The resulting ViewControllerFactory instance will use the Scene's key
to map
it to the proper layout, and invoke the wrapper
function to create a
ViewController for it.