Mapping Scenes to Layouts

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.

interface ViewControllerFactory {

    fun supports(scene: Scene<*>): Boolean
    fun viewControllerFor(scene: Scene<*>, parent: ViewGroup): ViewController
}

A ViewControllerFactory for a single Scene MyScene could for example look like this:

class MySceneViewControllerFactory : ViewControllerFactory {
    
    override fun supports(scene: Scene<*>) = scene is MyScene
    
    override fun viewControllerFor(scene: Scene<*>, parent: ViewGroup) : ViewController {
        return MySceneViewController(parent.inflate(R.layout.my_scene)
    }
}

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:

class MyScene<MyContainer> : Scene, ProvidesView {

    override fun createViewController(parent: ViewGroup): ViewController {
        return MyViewController(parent.inflate(R.layout.myscene))
    }
}

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.