Last active
April 29, 2020 19:10
-
-
Save marcellogalhardo/0b96bd1dc0755aa60ecd1d7eae8ec079 to your computer and use it in GitHub Desktop.
Coordinator base class.
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
package com.marcellogalhardo.coordinator | |
import androidx.fragment.app.Fragment | |
import androidx.fragment.app.FragmentFactory | |
import androidx.fragment.app.FragmentManager | |
import androidx.lifecycle.AbstractSavedStateViewModelFactory | |
import androidx.lifecycle.DefaultLifecycleObserver | |
import androidx.lifecycle.HasDefaultViewModelProviderFactory | |
import androidx.lifecycle.LifecycleOwner | |
import androidx.lifecycle.SavedStateHandle | |
import androidx.lifecycle.ViewModel | |
import androidx.lifecycle.ViewModelProvider | |
import androidx.lifecycle.observe | |
import kotlin.reflect.KClass | |
/** | |
* Base class used to control the instantiation of [Fragment] and [ViewModel] | |
* instances as well as coordinate the communication of both objects. | |
* | |
* Implementations can be registered with a [FragmentManager] via | |
* [FragmentManager.setFragmentFactory] using [Coordinator.fragmentFactory]. | |
*/ | |
abstract class Coordinator { | |
/** | |
* Create a new instance of a [Fragment] with the given [KClass]. This uses | |
* [FragmentFactory.loadFragmentClass] and the empty constructor of the | |
* result [Class] by default. | |
* | |
* @param fragmentClass The [KClass] of the [Fragment] to instantiate. | |
* @return Returns a new [Fragment] instance or null to use the default behaviour. | |
*/ | |
protected abstract fun <T : Fragment> createFragment( | |
fragmentClass: KClass<out T> | |
): Fragment? | |
/** | |
* Create a new instance of a [ViewModel] with the given [KClass]. This uses | |
* the [HasDefaultViewModelProviderFactory.getDefaultViewModelProviderFactory] | |
* of the current [Fragment] and the empty constructor of the [ViewModel] | |
* by default. | |
* | |
* @param modelClass The [KClass] of the [ViewModel] to instantiate. | |
* @return Returns a new fragment instance or null to use the default behaviour. | |
*/ | |
protected abstract fun <T : ViewModel> createViewModel( | |
key: String, | |
modelClass: KClass<out T>, | |
handle: SavedStateHandle | |
): ViewModel? | |
/** | |
* Called immediately after [Fragment.onCreateView] has returned, but before | |
* any saved state has been restored in to the view. | |
* | |
* This gives a chance to bind a [Fragment] with a [ViewModel] once their | |
* view hierarchy has been completely created. | |
* | |
* @param fragment The [Fragment] returned by [createFragment]. | |
* @param viewModelProvider A [ViewModelProvider] that delegates all calls | |
* to [createViewModel]. | |
*/ | |
protected abstract fun onViewCreated( | |
fragment: Fragment, | |
viewModelProvider: ViewModelProvider | |
) | |
/** | |
* Object used to integrate with [FragmentManager]. | |
* | |
* @see FragmentManager.setFragmentFactory | |
*/ | |
val fragmentFactory: FragmentFactory = CoordinatorFragmentFactory() | |
private val fragmentLifecycleObserver = FragmentLifecycleObserver() | |
private inner class CoordinatorFragmentFactory : FragmentFactory() { | |
@Suppress("UNCHECKED_CAST") | |
override fun instantiate(classLoader: ClassLoader, className: String): Fragment { | |
val fragmentClass = classLoader.loadClass(className) as Class<out Fragment> | |
val fragment = createFragment(fragmentClass.kotlin) | |
if (fragment != null) { | |
fragment.lifecycle.addObserver(fragmentLifecycleObserver) | |
return fragment | |
} | |
return super.instantiate(classLoader, className) | |
} | |
} | |
private inner class FragmentLifecycleObserver : DefaultLifecycleObserver { | |
override fun onCreate(owner: LifecycleOwner) { | |
val fragment = owner as Fragment | |
fragment.viewLifecycleOwnerLiveData.observe(fragment) { viewLifecycleOwner -> | |
val viewLifecycleObserver = ViewLifecycleObserver(fragment) | |
viewLifecycleOwner.lifecycle.addObserver(viewLifecycleObserver) | |
} | |
} | |
} | |
private inner class ViewLifecycleObserver( | |
private val fragment: Fragment | |
) : DefaultLifecycleObserver { | |
private val viewModelProviderFactory = ViewModelProviderFactory(fragment) | |
private val viewModelProvider = ViewModelProvider(fragment, viewModelProviderFactory) | |
override fun onCreate(owner: LifecycleOwner) { | |
onViewCreated(fragment, viewModelProvider) | |
} | |
} | |
private inner class ViewModelProviderFactory( | |
val fragment: Fragment | |
) : AbstractSavedStateViewModelFactory(fragment, null) { | |
@Suppress("UNCHECKED_CAST") | |
override fun <T : ViewModel> create( | |
key: String, | |
modelClass: Class<T>, | |
handle: SavedStateHandle | |
): T { | |
return createViewModel(key, modelClass.kotlin, handle) as? T | |
?: fragment.defaultViewModelProviderFactory.create(modelClass) | |
} | |
} | |
} |
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
package com.marcellogalhardo.coordinator | |
import androidx.fragment.app.Fragment | |
import androidx.fragment.app.FragmentManager | |
import androidx.lifecycle.observe | |
import androidx.lifecycle.SavedStateHandle | |
import androidx.lifecycle.ViewModel | |
import androidx.lifecycle.ViewModelProvider | |
import androidx.lifecycle.get | |
import com.marcellogalhardo.coordinator.Coordinator | |
import com.marcellogalhardo.coordinator.DaggerExampleComponent | |
import com.marcellogalhardo.coordinator.ExampleComponent | |
import com.marcellogalhardo.coordinator.ExampleFragment | |
import com.marcellogalhardo.coordinator.ExampleViewModel | |
import com.marcellogalhardo.coordinator.ExampleViewModelFactory | |
import kotlin.reflect.KClass | |
class ExampleCoordinator : Coordinator() { | |
// Create your component. | |
private val component = ExampleComponent.factory().create() | |
override fun <T : Fragment> createFragment( | |
fragmentClass: KClass<out T> | |
): Fragment? = when (fragmentClass) { | |
ExampleFragment::class -> component.exampleFragment | |
else -> null | |
} | |
override fun <T : ViewModel> createViewModel( | |
key: String, | |
modelClass: KClass<out T>, | |
handle: SavedStateHandle | |
): ViewModel? = when (modelClass) { | |
// SavedStateHandle is been "Assisted Injected" | |
ExampleViewModel::class -> component.exampleViewModelFactory.create(handle) | |
else -> null | |
} | |
override fun onViewCreated( | |
fragment: Fragment, | |
viewModelProvider: ViewModelProvider | |
) { | |
// Setup your Fragment and ViewModel(s). | |
if (fragment is ExampleFragment) { | |
val viewModel = viewModelProvider.get<ExampleViewModel>() | |
viewModel.state.observe(fragment.viewLifecycleOwner, fragment::onStateChanged) | |
viewModel.event.observe(fragment.viewLifecycleOwner, fragment::onEventChanged) | |
fragment.action.observe(fragment.viewLifecycleOwner, viewModel::onActionChanged) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment