Ошибка привязки данных при попытке передать 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()
    
Другие вопросы по тегам