Ошибка привязки данных при попытке передать ViewModel в макет включения с абстрактным типом переменной

*EDIT: Чтобы ответить на мой собственный вопрос, мне пришлось добавить EditorViewModel в качестве импорта в родительский абстрактный класс во внешнем макете и преобразовать viewModel в родительский класс, используя app:viewModel="@{( (EditorViewModel)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. файл: 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.

Большое спасибо за Вашу помощь. Может быть, мне повезет, и Джордж Маунт заглянет.


person AutoM8R    schedule 20.07.2017    source источник
comment
Это может быть ошибка. Я расследую это.   -  person George Mount    schedule 20.07.2017
comment
@GeorgeMount большое спасибо, я предоставлю полный код в частном порядке, если вы хотите его изучить. Я сделал недействительными все кеши в AS и удалил оба кеша .gradle, никаких изменений в поведении.   -  person AutoM8R    schedule 20.07.2017
comment
Я попробовал это с дженериками в базовом классе, как в описании, и не смог воспроизвести проблему. Репродукционный случай поможет. Это действительно должно работать.   -  person George Mount    schedule 20.07.2017
comment
Хм, сегодня наткнулся на другой проект, где мне нужно было сделать такой же кастинг... Интересно, это проблема с моей средой? Есть ли что-то, на что я должен сначала посмотреть на моей стороне?   -  person AutoM8R    schedule 18.10.2017


Ответы (2)


Просто для ясности, если кто-то столкнется с этим.

Добавлено 3 подхода к решению проблемы, №3 кажется лучшим

Похоже, это проблема с новым gradle 3 и более поздних версий со следующей ошибкой: не удается найти установщик для атрибута «приложение: 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>

включить/дочерний 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 (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" />

        <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>
person Francois    schedule 18.12.2017

Этот метод отлично работает

<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()
    
person RanaUmer    schedule 04.06.2021