Setting up LiveData observer in custom view without LifecycleOwner

12,905

Solution 1

1 Option - With good intention, you still have to do some manual work - like, calling onAttach\ onDetach Main purpose of Architecture components is to prevent doing this.

2 Option - In my opinion is better, but I would say it's a bit wrong to bind your logic around ViewModel and View. I believe you can do same logic inside Activity/Fragment without passing ViewModel and LifecycleOwner to CustomView. Single method updateData is enough for this purpose.

So, in this particular case, I would say it's overuse of Architecture Components.

Solution 2

it doesn't make sense to manage the lifecycle of the the view manually by passing some reference of the activity to the views and calling onAttach/onDetach, when we already have the context provided when that view is created.

I have a fragment in a NavigationView that has other fragments in a view pager, more like a nested fragment hierarchy scenario.

I have some custom views in these top-level fragments, when the custom view is directly in the top fragment, I can get an observer like this

viewModel.itemLiveData.observe((context as ContextWrapper).baseContext as LifecycleOwner,
   binding.item.text = "some text from view model"         
}

when I have the custom view as a direct child of an activity I set it up directly as

viewModel.itemLiveData.observe(context as LifecycleOwner,
   binding.item.text = "some text from view model"         
}

in these activities, if I have a fragment and it has some custom view and I use the 2nd approach, I get a ClassCastException(), and I have to reuse these custom views in different places, both activities, and fragments (that's the idea of having a custom view)

so i wrote an extension function to set the LifeCycleOwner

fun Context.getLifecycleOwner(): LifecycleOwner {
    return try {
        this as LifecycleOwner
    } catch (exception: ClassCastException) {
        (this as ContextWrapper).baseContext as LifecycleOwner
    }
}

now i simply set it everywhere as

viewModel.itemLiveData.observe(context.getLifecycleOwner(),
   binding.item.text = "some text from view model"         
}
Share:
12,905

Related videos on Youtube

kcrimi
Author by

kcrimi

Updated on June 04, 2022

Comments

  • kcrimi
    kcrimi about 2 years

    I'm trying out the new Android Architecture components and have run into a road block when trying to use the MVVM model for a custom view.

    Essentially I have created a custom view to encapsulate a common UI and it's respective logic to use throughout the app. I can set up the ViewModel in the custom view but then I'd have to either use observeForever() or manually set a LifecycleOwner in the custom view like below but neither seem correct.

    Option 1) Using observeForever()

    Activity

    class MyActivity : AppCompatActivity() {
    
        lateinit var myCustomView : CustomView
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            myCustomView = findViewById(R.id.custom_view)
            myCustomView.onAttach()
        }
    
        override fun onStop() {
            myCustomView.onDetach()
        }
    }
    

    Custom View

    class (context: Context, attrs: AttributeSet) : RelativeLayout(context,attrs){
    
        private val viewModel = CustomViewModel()
    
        fun onAttach() {
            viewModel.state.observeForever{ myObserver }
        }
    
        fun onDetach() {
            viewModel.state.removeObserver{ myObserver }
        }
    }
    

    Option 2) Setting lifecycleOwner from Activity`

    Activity

    class MyActivity : AppCompatActivity() {
    
        lateinit var myCustomView : CustomView
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            myCustomView = findViewById(R.id.custom_view)
            myCustomView.setLifeCycleOwner(this)
        }
    }
    

    Custom View

    class (context: Context, attrs: AttributeSet) : RelativeLayout(context,attrs){
    
        private val viewModel = CustomViewModel()
    
        fun setLifecycleOwner(lifecycleOwner: LifecycleOwner) {
            viewModel.state.observe(lifecycleOwner)
        }
    }
    

    Am I just misusing the patterns and components? I feel like there should be a cleaner way to compose complex views from multiple sub-views without tying them to the Activity/Fragment

    • Siamak
      Siamak almost 4 years
      Why you don't use observeForever(observer) instead? It does not require a LifeCycleOwner and you can remove Observer in onDetachedFromWindow()
  • Bitcoin Cash - ADA enthusiast
    Bitcoin Cash - ADA enthusiast over 4 years
    For people who like me use Custom Views instead of Fragments this is a problem. I will go with Option 1 as there is no way I am going back to Fragments and their LOL lifecycles.
  • user924
    user924 over 2 years
    a Fragment can have have two lifecycle owners: of fragment itself or of view of fragment (viewLifecycleOwner). In our case we need to get viewLifecycleOwner somehow if custom view was inflated in fragment