Android DataBinding & MVVM - Using same name for layout files in different layout folders

If anyone searches for this question, after 2 years I tried to do the same, and I saw it's working all fine now.

I created a layout file activity_main under layout and layout_sw600dp. Here's the layout under layout resources:

<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>

This one is the layout under layout_sw600dp folder:

<?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>

Both has a view but it has different id in each: small_square and big_square.

I run the project on phone & tablet. Here are my findings:

  • DataBinding creates an implementation that contains ALL views and variables under all layout files of same name in different layout folders.
  • Views that exists in all layouts are not nullable, all others are nullable. In above XML's, myRoot is not a nullable view when using binding from Kotlin, while big_square and small_square are nullable views. Variables are nullable whether or not they exists in all layouts ( which is expected behaviour ).
  • You cannot name binding classes different in each file. It has to be same ( MainBinding in above examples, or if you don't define it LayoutResourceName + Binding by default ).
  • Names for views and variables on binding implementation are camel case. So my small_variable & small_square was binding.smallVariable and binding.smallSquare on code side.
  • With Kotlin, you can just use views like binding.bigSquare?.operation, which is great that you don't need to check if it's tablet or phone or view is null or not beforehand.
  • Just a tip, you can assign binding fields even if layout that they are in won't be used. You can still say binding.smallVariable = 3 on code and it'll do the assignment and save the value. I think it's good to be careful.

I heavily use MVVM in my apps and am also building a library around it.

I follow the convention that there is a single ViewModel in every XML. Also, the name of the viewmodel variable is same in all XMLs.

So, in your case, you can create another ViewModel class that contains VMFirst and VMSecond.

public class ParentVM {
   VMFirst first;
   VMSecond second;
}

Both the XMLs (portrait and landscape) will have same names, say activity_main.xml.

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

Then no check is required in MainActivity code.

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

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

This works.

Advantages of single ViewModel

In fact, because I follow same variable name throughout all xmls, I am able to include the binding logic in a base class MvvmActivity itself. So, all my activities look like:

public class MainActivity extends MvvmActivity {

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

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

MvvmActivity implementation: MvvmActivity.java

Another advantage of keeping a constant data binding variable is that you can setup RecyclerView or ViewPager adapters in XML itself. See Setup RecyclerView from XML for more details.