Я пробую новые компоненты архитектуры Android и столкнулся с препятствием при попытке использовать модель MVVM для настраиваемого представления.
По сути, я создал настраиваемое представление для инкапсуляции общего пользовательского интерфейса и соответствующей логики для использования во всем приложении. Я могу настроить ViewModel в настраиваемом представлении, но тогда мне придется либо использовать observeForever(), либо вручную установить LifecycleOwner в настраиваемом представлении, как показано ниже, но ни один из них не кажется правильным.
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 }
}
}
Деятельность
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.
1 вариант -
С добрыми намерениями вам все равно придется проделать некоторую ручную работу - например, вызвать в onAttach \ onDetach. Основная цель компонентов архитектуры - предотвратить это.
2 Вариант -
На мой взгляд, лучше, но я бы сказал, что связывать свою логику с ViewModel и View немного неправильно. Я считаю, что вы можете делать ту же логику внутри Activity/Fragment, не передавая ViewModel и LifecycleOwner в CustomView. Для этого достаточно одного метода updateData.
Итак, в данном конкретном случае я бы сказал, что это чрезмерное использование компонентов архитектуры.
Для людей, которые, как и я, используют пользовательские представления вместо фрагментов, это проблема. Я выберу вариант 1, так как я не вернусь к фрагментам и их жизненным циклам LOL.
Нет смысла управлять жизненным циклом представления вручную, передавая некоторую ссылку на действие представлениям и вызывая 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"
}
Почему вы не используете вместо этого
observeForever(observer)? Для этого не требуется LifeCycleOwner, и вы можете удалить Observer вonDetachedFromWindow().