Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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:

  1. Using layout and layout-land folders ( Failed, error is explained above )

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

like image 409
Mel Avatar asked Sep 07 '16 08:09

Mel


People also ask

What is the use of DataBinding in Android?

Data Binding allows you to effortlessly communicate across views and data sources. This pattern is important for many Android designs, including model view ViewModel (MVVM), which is currently one of the most common Android architecture patterns.

Is Android DataBinding deprecated?

Recently Android has announced that with Kotlin 1.4. 20, their Android Kotlin Extensions Gradle plugin will be deprecated and will no longer be shipped in the future Kotlin releases. Android Kotlin Extensions plugin brought with it two very cool features : Synthetics let you replace calls to findViewById with kotlinx.

What is difference between DataBinding and ViewBinding in Android?

View binding and data binding both generate binding classes that you can use to reference views directly. However, view binding is intended to handle simpler use cases and provides the following benefits over data binding: Faster compilation: View binding requires no annotation processing, so compile times are faster.


2 Answers

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.
like image 87
Mel Avatar answered Oct 10 '22 13:10

Mel


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.

like image 2
Manas Chaudhari Avatar answered Oct 10 '22 13:10

Manas Chaudhari