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, whilebig_square
andsmall_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 itLayoutResourceName
+Binding
by default ). - Names for views and variables on binding implementation are camel case. So my
small_variable
&small_square
wasbinding.smallVariable
andbinding.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 saybinding.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.