Android DataBinding & MVVM - Использование одного и того же имени для файлов макетов в разных папках макетов

Я разрабатываю приложение со связыванием данных и MVVM.

Я пытаюсь использовать альтернативный макет для своего приложения в ландшафтном режиме. У меня есть:

layout/fragment_content.xml
layout-land/fragment_content.xml

Оба макета имеют одинаковые виды с разным взглядом и получают каналы с одинаковых моделей просмотров, например:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

<data class="MyBinding">

    <variable
        name="viewModel"
        type="com.myapp.package.viewModel.VMFirst"/>

    <variable
        name="controlModel"
        type="com.myapp.package.viewModel.VMSecond"/>
</data>

<DIFFERENT CONTENT HERE>

Все представления и id существуют в обоих макетах.

Ну, проблема в том, что она не компилируется, ошибка просто "cannot find symbol method getViewModel" и getter для другой переменной.

То, что я пробовал до сих пор:

Нельзя ли использовать привязку привязки данных к тегу data в нескольких файлах макета? Что я должен использовать для использования разных макетов для разных состояний при использовании привязки данных? Спасибо!

Изменить: удаление class="MyBinding" не изменило ошибок.

Ответ 1

Если кто-то ищет этот вопрос, через 2 года я попытался сделать то же самое, и теперь я вижу, что он работает нормально.

Я создал файл макета activity_main в layout и layout_sw600dp. Вот расположение под ресурсами layout:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <variable
        name="small_variable"
        type="Integer"/>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/myRoot"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <View
            android:id="@+id/small_square"
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:background="@android:color/holo_blue_bright"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

Это макет в папке layout_sw600dp:

<?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"
    xmlns:tools="http://schemas.android.com/tools">

    <variable
        name="big_variable"
        type="Long"/>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/myRoot"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <View
            android:id="@+id/big_square"
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:background="@android:color/holo_blue_bright"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

У обоих есть представление, но в каждом есть разные идентификаторы: small_square и big_square.

Я запускаю проект на телефоне и таблетки. Вот мои выводы:

  • DataBinding создает реализацию, которая содержит ВСЕ представления и переменные во всех файлах макетов с одинаковыми именами в разных папках макетов.
  • Представления, существующие во всех макетах, не могут быть обнуляемыми, все остальные обнуляются В приведенных выше XML myRoot не является обнуляемым представлением при использовании привязки из Kotlin, в то время как big_square и small_square являются обнуляемыми представлениями. Переменные обнуляются независимо от того, существуют ли они во всех макетах (что является ожидаемым поведением).
  • Вы не можете назвать разные классы привязки в каждом файле. Он должен быть таким же (MainBinding в приведенных выше примерах или если вы не определите его LayoutResourceName + Binding по умолчанию).
  • Имена для представлений и переменных при реализации привязки являются верблюжьим регистром. Так что мой small_variable & small_square был binding.smallVariable и binding.smallSquare на стороне кода.
  • С Kotlin вы можете просто использовать представления, такие как binding.bigSquare?.operation, и это здорово, что вам не нужно заранее проверять, является ли планшет или телефон или просмотр нулевым или нет.
  • Просто совет, вы можете назначить поля binding, даже если макет, в котором они находятся, не будет использоваться. Вы все еще можете сказать binding.smallVariable = 3 в коде, и он выполнит присваивание и сохранит значение. Я думаю, хорошо быть осторожным.

Ответ 2

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

Я согласен с тем, что в каждом XML есть один ViewModel. Кроме того, имя переменной viewmodel одинаково во всех XML файлах.

Итак, в вашем случае вы можете создать еще один класс ViewModel, содержащий VMFirst и VMSecond.

public class ParentVM {
   VMFirst first;
   VMSecond second;
}

Оба XML (портрет и пейзаж) будут иметь одинаковые имена, например activity_main.xml.

<layout>
    <data>
      <variable 
          type="ParentViewModel"
          name="vm"/>
    </data>

Тогда в коде MainActivity не требуется проверка.

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ViewDataBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
    binding.setVariable(BR.vm, new ParentViewModel());
}

Это работает.

Преимущества одиночного ViewModel

На самом деле, поскольку я следую за одним и тем же именем переменной во всех xmls, я могу включить логику привязки в базовый класс MvvmActivity. Итак, все мои действия выглядят так:

public class MainActivity extends MvvmActivity {

    @NonNull
    @Override
    protected ViewModel createViewModel() {
        return new MainViewModel();
    }

    @Override
    protected int getLayoutId() {
        return R.layout.activity_main;
    }
}

Реализация MvvmActivity: MvvmActivity.java

Еще одно преимущество сохранения постоянной переменной привязки данных заключается в том, что вы можете настроить адаптеры RecyclerView или ViewPager в самом XML. Подробнее см. Настройка RecyclerView из XML.

Ответ 3

По умолчанию класс Binding будет сгенерирован на основе имени файла макета, преобразуя его в регистр Pascal и суффиксом "привязки" к нему. Вышеупомянутый файл макета был main_activity.xml, поэтому класс generate был MainActivityBinding. - Данные привязки

и генерируется во время компиляции.

поэтому, выберите другой макет по java-коду.

layout/
R.layout.activity_main
R.layout.activity_main_tablet

values/
    <bool name="is_mobile">true</bool>
    <bool name="is_tablet">false</bool>
values-w820dp/
    <bool name="is_mobile">false</bool>
    <bool name="is_tablet">true</bool>



@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if(getResources().getBoolean(R.bool.is_mobile)) {
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);            
    } else {
        ActivityMainTabletBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main_tablet);   
    }

}