Настройка наблюдателя LiveData в пользовательском представлении без LifecycleOwner

Я пробую новые компоненты архитектуры Android и столкнулся с препятствием при попытке использовать модель MVVM для настраиваемого представления.

По сути, я создал настраиваемое представление для инкапсуляции общего пользовательского интерфейса и соответствующей логики для использования во всем приложении. Я могу настроить ViewModel в настраиваемом представлении, но тогда мне придется либо использовать observeForever(), либо вручную установить LifecycleOwner в настраиваемом представлении, как показано ниже, но ни один из них не кажется правильным.

Вариант 1) Использование observeForever()

Деятельность

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()
    }
}

Пользовательский вид

class (context: Context, attrs: AttributeSet) : RelativeLayout(context,attrs){

    private val viewModel = CustomViewModel()

    fun onAttach() {
        viewModel.state.observeForever{ myObserver }
    }

    fun onDetach() {
        viewModel.state.removeObserver{ myObserver }
    }
}

Вариант 2) Установка lifecycleOwner из 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)
    }
}

Пользовательский вид

class (context: Context, attrs: AttributeSet) : RelativeLayout(context,attrs){

    private val viewModel = CustomViewModel()

    fun setLifecycleOwner(lifecycleOwner: LifecycleOwner) {
        viewModel.state.observe(lifecycleOwner)
    }
}

Я просто неправильно использую шаблоны и компоненты? Я чувствую, что должен быть более чистый способ составлять сложные представления из нескольких подвидов, не привязывая их к Activity / Fragment.

Почему вы не используете вместо этого observeForever(observer)? Для этого не требуется LifeCycleOwner, и вы можете удалить Observer в onDetachedFromWindow().

Siamak 31.08.2020 17:38
21
1
10 778
2

Ответы 2

1 вариант - С добрыми намерениями вам все равно придется проделать некоторую ручную работу - например, вызвать в onAttach \ onDetach. Основная цель компонентов архитектуры - предотвратить это.

2 Вариант - На мой взгляд, лучше, но я бы сказал, что связывать свою логику с ViewModel и View немного неправильно. Я считаю, что вы можете делать ту же логику внутри Activity/Fragment, не передавая ViewModel и LifecycleOwner в CustomView. Для этого достаточно одного метода updateData.

Итак, в данном конкретном случае я бы сказал, что это чрезмерное использование компонентов архитектуры.

Для людей, которые, как и я, используют пользовательские представления вместо фрагментов, это проблема. Я выберу вариант 1, так как я не вернусь к фрагментам и их жизненным циклам LOL.

Bitcoin Cash - ADA enthusiast 13.11.2019 07:32

Нет смысла управлять жизненным циклом представления вручную, передавая некоторую ссылку на действие представлениям и вызывая onAttach / onDetach, когда у нас уже есть контекст, предоставленный при создании этого представления.

У меня есть фрагмент в NavigationView, у которого есть другие фрагменты в пейджере представления, больше похоже на сценарий иерархии вложенных фрагментов.

У меня есть несколько настраиваемых представлений в этих фрагментах верхнего уровня, когда настраиваемое представление находится непосредственно в верхнем фрагменте, я могу получить такого наблюдателя

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

когда у меня есть настраиваемое представление в качестве прямого дочернего элемента действия, я настраиваю его напрямую как

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

в этих действиях, если у меня есть фрагмент и у него есть какое-то настраиваемое представление, и я использую второй подход, я получаю ClassCastException (), и мне приходится повторно использовать эти настраиваемые представления в разных местах, как действиях, так и фрагментах (это идея иметь собственный вид)

поэтому я написал функцию расширения, чтобы установить LifeCycleOwner

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

теперь я просто устанавливаю его везде как

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

Другие вопросы по теме