Skip to content

Instantly share code, notes, and snippets.

@alashow
Last active April 1, 2019 05:18
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 alashow/67cc52c606ddc7637e75185673b7f9a6 to your computer and use it in GitHub Desktop.
Save alashow/67cc52c606ddc7637e75185673b7f9a6 to your computer and use it in GitHub Desktop.
ViewModel dynamic parameters hack
class AppViewModelFactory @Inject constructor(
private val creators: @JvmSuppressWildcards Map<Class<out ViewModel>, Provider<ViewModel>>
) : ViewModelProvider.Factory {
var onViewModelCreate: ((viewModel: ViewModel) -> Unit)? = null
override fun <T : ViewModel> create(modelClass: Class<T>): T {
var creator: Provider<out ViewModel>? = creators[modelClass]
if (creator == null) {
for ((key, value) in creators) {
if (modelClass.isAssignableFrom(key)) {
creator = value
break
}
}
}
if (creator == null) {
throw IllegalArgumentException("unknown model class " + modelClass)
}
try {
@Suppress("UNCHECKED_CAST")
val vm = creator.get() as T
onViewModelCreate?.invoke(vm)
return vm
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
abstract class BaseVmActivity<VM : ViewModel> : AppCompatActivity() {
@Inject
lateinit var viewModelFactory: AppViewModelFactory
protected lateinit var vm: VM
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModelFactory.onViewModelCreate = { viewModel ->
if (viewModel.javaClass == getVmClass()) {
@Suppress("UNCHECKED_CAST")
initVm(viewModel as VM)
}
}
vm = getVm(getVmClass())
}
/**
* Called after [VM]s first time creation.
* Can be used for setting initial states of view model.
*/
protected open fun initVm(viewModel: VM) {}
/**
* @return vm class
*/
protected abstract fun getVmClass(): Class<VM>
protected open fun <S : ViewModel> getVm(clazz: Class<S>): S {
return ViewModelProviders.of(this, viewModelFactory).get(clazz)
}
}
class FeatureActivity : BaseVmActivity<FeatureViewModel>() {
override fun initVm(viewModel: FeatureViewModel) {
viewModel.init("some-data")
}
}
@Module(
includes = [
FeatureBinders::class
]
)
class FeatureModule
@Module
interface FeatureBinders {
@ContributesAndroidInjector
fun FeatureActivity(): FeatureActivity
@Binds
@IntoMap
@ViewModelKey(FeatureViewModel::class)
fun featureViewModel(vm: FeatureViewModel): ViewModel
}
class FeatureViewModel @Inject constructor(val featureRepository: FeatureRepository) : ViewModel() {
fun init(data: String) {
// use data somehow
}
}
@Module
abstract class ViewModelBuilder {
@Binds
internal abstract fun bindViewModelFactory(factory: AppViewModelFactory): ViewModelProvider.Factory
}
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment