I would like to refactor my Custom View to use android architecture components. However, I see that
ViewModelProviders.of(...)
takes only Activity or fragment. Any idea how to make it work? Should I use fragment instead of Custom View?
You can use findViewTreeViewModelStoreOwner() to retrieve a ViewModel from within your custom View.
And then implement DefaultLifecycleObserver in that view so you can hook onto lifecycle methods.
Those 2 combined should give you all the ViewModel and Scope options you get with Activity and Fragment implementations`.
Custom View code, here it's based on a ConstraintLayout and I've added KoinViewModelFactory so that your dependencies can be injected via the constructor. Otherwise just use ViewModelProvider(owner).
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.findViewTreeViewModelStoreOwner
import androidx.lifecycle.viewModelScope
import org.koin.androidx.viewmodel.factory.KoinViewModelFactory
import org.koin.core.component.getScopeId
import org.koin.core.qualifier.named
import org.koin.mp.KoinPlatform.getKoin
class MyCustomView : ConstraintLayout, DefaultLifecycleObserver {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
private val viewModel by lazy {
findViewTreeViewModelStoreOwner()?.let { owner ->
ViewModelProvider(
owner,
KoinViewModelFactory(
kClass = MyCustomViewModel::class,
scope = getKoin().getOrCreateScope(scopeId = owner.getScopeId(), named(owner.getScopeId())),
)
)[MyCustomViewModel::class.java]
}
}
init {
inflate(context, R.layout.my_custom_view, this)
// makes this view lifecycle aware
(context as? LifecycleOwner)?.lifecycle?.addObserver(this)
}
override fun onCreate(owner: LifecycleOwner) {
super.onCreate(owner)
// observe changes
viewModel?.events?.observeNonNull(owner) { handleEvent(it) }
}
override fun onDestroy(owner: LifecycleOwner) {
// other lifecycle methods like this are available...
}
fun handleEvent(event: UiEvent) {}
}
ViewModel implementation
import androidx.lifecycle.ViewModel
import androidx.lifecycle.MutableLiveData
import org.koin.core.component.KoinComponent
class MyCustomViewModel(
private val injectedService: SomeService, // will be injected
) : ViewModel(), KoinComponent {
val events = MutableLiveData<UiEvent>() // live data example
fun someAction() {
events.value = UiEvent.SomeAction
}
sealed class UiEvent {
data object SomeAction : UiEvent()
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With