How should I observe a LiveData in a Custom View. I tried to cast it's context as lifecycleOwner but it makes some problems and doesn't work in all cases. I tried to put a setter but it doesn't work either
Views do not have lifecycle on their own. There are 3 approaches that I personally use, they are actually the same thing but one of them is adding a lifecycle while the others are without a lifecycle.
class MyCustomView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
): View(context, attrs, defStyleAttr){
val myObserver = Observer<Long>{
//whatever
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
liveData.observeForever(myObserver)
}
override fun onDetachFromWindow() {
super.onDetachFromWindow()
liveData.removeObserver(myObserver)
}
}
This method manually observe/remove on attach/detach to window. I prefer it when I'm observing few livedata and it's simple/limited
Another option is to turn our custom view into a LifecycleOwner. I recommend this method for BaseCustomViews and some extremely huge and complicated views (like a Map navigation view). Also, remember that you need to manually notify the view about its parent getting destroyed (you can call the method directly or use a view tree navigation to call this function for all view which are the child of BaseCustomView).
abstract class BaseCustomView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
): View(context, attrs, defStyleAttr), LifecycleOwner {
protected val lifecycleRegistry = LifecycleRegistry(this);
override fun getLifecycle() = lifecycleRegistry
override fun onAttachedToWindow() {
super.onAttachedToWindow()
lifecycleRegistry.currentState = Lifecycle.State.RESUMED
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
lifecycleRegistry.currentState = Lifecycle.State. CREATED
}
@CallSuper
open fun destroyLifecycle(){
lifecycleRegistry.currentState = Lifecycle.State.DESTROYED
}
val myObserver = Observer<Long>{
//whatever
}
init{
liveData.observe(this, myObserver}
}
}
If you prefer the first method, another option can be combining this 2 ideas, having a BaseCustomView
which enables its children to observe LiveData easily.
abstract class BaseCustomView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
): RelativeLayout(context, attrs, defStyleAttr) {
//a list to hold the list of observers and their LiveData
private val autoObservers = ArrayList<Pair<LiveData<*>, Observer<*>>>()
override fun onAttachedToWindow() {
super.onAttachedToWindow()
for((liveData, observer) in autoObservers){
liveData.observeForever(observer as Observer<in Any>)
}
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
for((liveData, observer) in autoObservers){
liveData.removeObserver(observer as Observer<in Any>)
}
}
protected fun<T : Any> LiveData<T>.observe( observer: Observer<T> ){
autoObservers.add(this to observer)
//if it's not attached, onAttachedToWindow will do the observation
if(isAttachedToWindow){
observeForever(observer)
}
}
}