I've been developing an app with data binding & MVVM.
I'm trying to use an alternative layout for my app on landscape mode. I have:
layout/fragment_content.xml layout-land/fragment_content.xml
Both layouts have same views with different look, and get feeds from same view models, like this:
<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>
All the views and id's exist in both layouts.
Well, problem is, it doesn't compile, error is simply "cannot find symbol method getViewModel"
and getter for the other variable.
What I tried so far:
Using layout and layout-land folders ( Failed, error is explained above )
Using layout aliases Use Layout Aliases which I found here Issue 199344: Data binding does not work with layout aliases. I didn't change anything in xml files while trying this approach. This also failed, error is Could not write to com.myapp.package.databinding.MyBinding
Is it not possible to use data binding data
tag in multiple layout files ? What should I use to use different layouts for different states while using data binding ? Thanks !
Edit: deleting class="MyBinding"
did not change errors.
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.
By default, a Binding class will be generated based on the name of the layout file, converting it to Pascal case and suffixing "Binding" to it. The above layout file was main_activity.xml so the generate class was MainActivityBinding. --Binding Data
and generated at compile time.
so, select different layout by java code.
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); } }