Ошибка привязки данных при попытке передать ViewModel в макет включения с абстрактным типом переменной
* РЕДАКТИРОВАТЬ: Чтобы ответить на мой собственный вопрос, мне пришлось добавить EditorViewModel в качестве импорта в родительский абстрактный класс во внешнем макете, и преобразовать viewModel в родительский класс, используя app:viewModel="@{((EditorViewModel)viewModel)}" Вот и все! Клянусь, я не помню, как делал этот актерский состав раньше... *
Моя проблема возникает из-за того, что включенный макет определяет тип, который является родительским абстрактным классом, а НЕ дочерним конкретным классом viewModel, внешний макет пытается поделиться с включенным макетом.
Я подтвердил, что изменение типа включенного макета на тип дочернего конкретного класса решает проблему, однако, оно должно работать с абстрактным типом класса...
Вот как я определяю свою переменную viewModel, это конкретный класс в приведенном ниже типе:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:bind="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.ootpapps.gpeofflinedatacollection.viewmodels.EquipmentEditorViewModel" />
</data>
...
Несколько утверждений позже я включил макет, пытаясь поделиться viewModel сверху:
<include
layout="@layout/layout_spinner_location"
app:viewModel="@{viewModel}" />
Внутри включенного макета переменная viewModel определяется следующим образом:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bind="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewModel"
type="com.ootpapps.gpeofflinedatacollection.viewmodels.EditorViewModel" />
</data>
И, конечно, вот EquipmentEditorViewModel, показывающий, что он расширяет абстрактный родительский класс EditorViewModel (определенный как тип родительского класса выше во включенном макете):
public class EquipmentEditorViewModel extends EditorViewModel<Equipment> {
Я получаю ошибку:
**** / ошибка привязки данных ****msg: не удается найти установщик для атрибута app: viewModel с типом параметра com.ootpapps.gpeofflinedatacollection.viewmodels.EquipmentEditorViewModel для com.ootpapps.gpeofflinedatacollection.databinding.LayoutSpinnerLocationBinding. file:C:\Users\Ryan\AndroidstudioProjects\GPEOfflineDataCollection\app\src\main\res\layout\content_equipment_editor.xml loc:31:33 - 31:41 ****\ ошибка привязки данных ****
Как я упоминал выше, изменение типа layout_spinner_location на "EquipmentEditorViewModel" исправляет ошибку, ОДНАКО мне нужно использовать абстрактный тип для повторного использования этого представления, так как оно не всегда использует EquipmentEditorViewModel, иногда для этого требуется "ToolEditorViewModel"или"MeasurmentEditorViewModel", все из которых расширяют EditorViewModel.
Большое спасибо за Вашу помощь. Может быть, мне повезет, и Джордж Маунт зайдет.
2 ответа
Просто для ясности, если кто сталкивался с этим.
Добавлено 3 подхода для решения проблемы, # 3 кажется победителем
Кажется, это проблема с новым Gradle 3 и выше со следующей ошибкой: Не удается найти установщик для атрибута 'app:viewModel'
До
родительский xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewModel"
type="com.myapp.android.viewmodel.base.PageWebViewViewModel" />
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:orientation="vertical">
<WebView
android:id="@+id/webview_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="@{viewModel.contentVisibility}"
app:baseUrl="@{null}"
app:builtInZoomControlsEnabled="@{false}"
app:cacheMode="@{viewModel.webViewCacheMode}"
app:data="@{viewModel.webViewData}"
app:domStorageEnabled="@{true}"
app:encoding="@{`UTF-8`}"
app:headers="@{viewModel.headers}"
app:javaScriptEnabled="@{true}"
app:loadUrl="@{viewModel.webViewUrl}"
app:mimeType="@{`text/html; charset=utf-8`}"
app:webChromeClient="@{viewModel.webChromeClient}"
app:webInterface="@{viewModel.webInterface}"
app:webInterfaceName="@{viewModel.webInterfaceName}"
app:webViewClient="@{viewModel.webViewClient}" />
<include
layout="@layout/request_error_view"
app:viewModel="@{viewModel}" />
<include
android:id="@+id/lyt_loading"
layout="@layout/loading_layout"
app:viewModel="@{viewModel}" />
</FrameLayout>
</layout>
include / child xml (layout / request_error_view)
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable
name="viewModel"
type="com.myapp.android.viewmodel.base.BaseViewModel" />
</data>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/send_request_error_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical"
android:visibility="@{viewModel.errorVisibility}">
<TextView
android:id="@+id/text_error_sending_request"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/error_sending_request"
android:textColor="@color/window_primary_text"
android:textSize="@dimen/text_size_large" />
<Button
android:id="@+id/button_try_again"
style="@style/SuperbalistButton.Green.NoInset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/standard_content_margin"
android:onClick="@{viewModel::onClickReload}"
android:text="@string/try_again" />
</LinearLayout>
</layout>
После #1 (подход ко второй переменной, похоже, не работает)
родительский xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewModel"
type="com.myapp.android.viewmodel.base.PageWebViewViewModel" />
<variable
name="viewModelBase"
type="com.myapp.android.viewmodel.base.BaseViewModel" />
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:orientation="vertical">
<WebView
android:id="@+id/webview_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="@{viewModel.contentVisibility}"
app:baseUrl="@{null}"
app:builtInZoomControlsEnabled="@{false}"
app:cacheMode="@{viewModel.webViewCacheMode}"
app:data="@{viewModel.webViewData}"
app:domStorageEnabled="@{true}"
app:encoding="@{`UTF-8`}"
app:headers="@{viewModel.headers}"
app:javaScriptEnabled="@{true}"
app:loadUrl="@{viewModel.webViewUrl}"
app:mimeType="@{`text/html; charset=utf-8`}"
app:webChromeClient="@{viewModel.webChromeClient}"
app:webInterface="@{viewModel.webInterface}"
app:webInterfaceName="@{viewModel.webInterfaceName}"
app:webViewClient="@{viewModel.webViewClient}" />
<include
layout="@layout/request_error_view"
app:viewModel="@{viewModelBase}" />
<include
android:id="@+id/lyt_loading"
layout="@layout/loading_layout"
app:viewModel="@{viewModelBase}" />
</FrameLayout>
</layout>
После #2 (подход к кастингу, работает)
родительский xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewModel"
type="com.myapp.android.viewmodel.base.PageWebViewViewModel" />
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:orientation="vertical">
<WebView
android:id="@+id/webview_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="@{viewModel.contentVisibility}"
app:baseUrl="@{null}"
app:builtInZoomControlsEnabled="@{false}"
app:cacheMode="@{viewModel.webViewCacheMode}"
app:data="@{viewModel.webViewData}"
app:domStorageEnabled="@{true}"
app:encoding="@{`UTF-8`}"
app:headers="@{viewModel.headers}"
app:javaScriptEnabled="@{true}"
app:loadUrl="@{viewModel.webViewUrl}"
app:mimeType="@{`text/html; charset=utf-8`}"
app:webChromeClient="@{viewModel.webChromeClient}"
app:webInterface="@{viewModel.webInterface}"
app:webInterfaceName="@{viewModel.webInterfaceName}"
app:webViewClient="@{viewModel.webViewClient}" />
<include
layout="@layout/request_error_view"
app:viewModel="@{((com.myapp.android.viewmodel.base.BaseViewModel) viewModel)}" />
<include
android:id="@+id/lyt_loading"
layout="@layout/loading_layout"
app:viewModel="@{((com.myapp.android.viewmodel.base.BaseViewModel) viewModel)}" />
</FrameLayout>
</layout>
После #3 (подход импорта и приведения, работает)
родительский xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<import type="com.myapp.android.viewmodel.base.BaseViewModel" />
<variable
name="viewModel"
type="com.myapp.android.viewmodel.base.PageWebViewViewModel" />
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:orientation="vertical">
<WebView
android:id="@+id/webview_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="@{viewModel.contentVisibility}"
app:baseUrl="@{null}"
app:builtInZoomControlsEnabled="@{false}"
app:cacheMode="@{viewModel.webViewCacheMode}"
app:data="@{viewModel.webViewData}"
app:domStorageEnabled="@{true}"
app:encoding="@{`UTF-8`}"
app:headers="@{viewModel.headers}"
app:javaScriptEnabled="@{true}"
app:loadUrl="@{viewModel.webViewUrl}"
app:mimeType="@{`text/html; charset=utf-8`}"
app:webChromeClient="@{viewModel.webChromeClient}"
app:webInterface="@{viewModel.webInterface}"
app:webInterfaceName="@{viewModel.webInterfaceName}"
app:webViewClient="@{viewModel.webViewClient}" />
<include
layout="@layout/request_error_view"
app:viewModel="@{(BaseViewModel) viewModel)}" />
<include
android:id="@+id/lyt_loading"
layout="@layout/loading_layout"
app:viewModel="@{((BaseViewModel) viewModel)}" />
</FrameLayout>
</layout>
Этот метод отлично работает
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewmodel"
type="com.myapp.data.ViewModel" />
</data>
<include
android:id="@+id/include"
layout="@layout/buttons_layout" />
</layout>
buttons_layout
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewmodel"
type="com.myapp.data.ViewModel" />
</data>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{() -> viewmodel.onClickChangeName()}"
android:text="Change Name"
app:layout_constraintTop_toBottomOf="@id/include"
app:layout_constraintStart_toStartOf="parent"
/>
</layout>
Основная деятельность
lateinit var dataBinding: ActivityMainBinding
lateinit var viewModel: viewModel
dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
viewModel = ViewModelProvider(this).get(ViewModel::class.java)
dataBinding.include.viewModel = viewModel
dataBinding.viewModel = viewModel
dataBinding.executePendingBindings()