Summary of Android Data Binding (II)

Posted by Carlo Gambino on Thu, 27 Jun 2019 20:16:47 +0200

Preface

In my last article, I briefly learned about the environment configuration and basic usage of DataBinding:

Summary of Android Data Binding (I)

This article summarizes the basic ways of using Google's official documents.

Basic data binding

1. Objects (JavaBean s) can be created in this way:

/**
 * Created by QingMei on 2017/5/21 21:31
 * email:mei_husky@qq.com
 * desc:Used to
 */
 public class Student{

    public Student(String name, int age) {
        this.name.set(name);
        this.age = age;
    }

    public int age;
    public String name ;

}

2. In the layout file, set:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:bind="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <!--DataBinding Support AutoComplete,therefore type Fill Student,You can do the same as in the code.-->
        <!--The user variable within data describes a property that may be used within this layout.-->
        <variable
            name="student"
            type="com.mei_husky.samplemvvm.model.Student" />

        <variable
            name="presenter"
            type="com.mei_husky.samplemvvm.view.activity.DataBindingBaseActivity.Presenter" />

        <import
            alias="Utils"
            type="com.mei_husky.samplemvvm.util.MyUtils" />

        <import type="java.util.List" />

    </data>

    <LinearLayout
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:orientation="vertical"
      tools:context="com.mei_husky.samplemvvm.view.activity.DataBindingBaseActivity">

      <!--Fixed characters we can use `` Package, corresponding, external""package-->
      <Button
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:onClick="@{() -> presenter.onNameClick(student.name)}"
          android:text="@{`Student FirstName :` + student.name}"
          android:textAllCaps="false"
          android:textSize="14sp" />

      <!--Use MyUtils Tool class(alias Utils),Return in callback method age+1 -->
      <Button
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:onClick="@{() ->  presenter.onAgeClick(Utils.addAge(student.age))}"
          android:text="@{`Student Age :` + student.age}"
          android:textAllCaps="false"
          android:textSize="14sp" />

    </LinearLayout>
</layout>

3. Then in activity:

public class DataBindingBaseActivity extends AppCompatActivity {

     private ActivityDataBindingBaseBinding binding;

     private Student student;

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

     private void inject() {
         binding = DataBindingUtil.setContentView(this, R.layout.activity_data_binding_base);

         student = new Student("qingMei2", 12);
         contents.add("content -> 0");
         contents.add("content -> 1");

  //        There are two ways to assign student s to XML
  //        binding.setStudent(student);
          binding.setVariable(BR.student, student);
     }

     public class Presenter {

         public void onNameClick(String Name) {
             Toast.makeText(DataBindingBaseActivity.this, Name, Toast.LENGTH_SHORT).show();
         }


         public void onAgeClick(int age) {
             Toast.makeText(DataBindingBaseActivity.this, String.valueOf(age), Toast.LENGTH_SHORT).show();
         }

      }

}

Layout Details

1. We can add import tag to the data tag of xml to guide the package, so that we can call the method of the corresponding class directly.

<!--wrong lang Classes of packages need to be import(For example, the tool class, and then you can use its methods-->
<import
    alias="Utils"   //alias
    type="com.mei_husky.samplemvvm.util.MyUtils" />

<import type="java.util.List" />

2. Data Binding binds include tags

Include tags are often used for reusable layout. Of course, we can also use data Binding to bind data. First, we create include tags.

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable
            name="studentInner"
            type="com.mei_husky.samplemvvm.model.Student" />

    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:gravity="center"
            android:text="@{`include display StudentName : ` + studentInner.name}"
            android:layout_height="wrap_content" />

    </LinearLayout>
</layout>

Then, using the include tag in xml, the student is passed to the studentInner tag in the include:

<include
    layout="@layout/include_bind_views"
    bind:studentInner="@{student}" />

3. The grammar we can use in xml:

Common Features
The expression language looks a lot like a Java expression. These are the same:
Mathematical + - / * %
String concatenation +
Logical && ||
Binary & | ^
Unary + - ! ~
Shift >> >>> <<
Comparison == > < >= <=
instanceof
Grouping ()
Literals - character, String, numeric, null
Cast
Method calls
Field access
Array access []
Ternary operator ?:

For example:

android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'

4. However, these grammars are not supported in the layout file for Databinding:

this

super

new

Explicit generic invocation(Display generic calls)

But we can use generics by escaping:

<!--Generic support will give a red line at compile time, but it can run directly,But you need to escape characters, such as:&lt;data type> perhaps&lt;data type&gt;-->
       <variable
           name="contents"
           type="List&lt;String&gt;" />

5. There are some additions:

<!--Fixed characters can also be used "" Package, corresponding, external``package-->
<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_horizontal"
    android:text='@{"list :"+contents[0]}' />

<!--Reference resource files-->
<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_horizontal"
    android:text="@{`Reference resource files appName -> ` + @string/app_name}" />

Data Objects

Now let's deepen the use of beans in dataBinding to achieve dynamic binding. When the data source changes, the corresponding data in the UI changes at the same time:
First is the Student class:

//First inherit the BaseObservale class
public class Student extends BaseObservable{

    public Student(String name, int age) {
        this.name.set(name);
        this.age = age;
    }

    /**
     * ObservableObject Implementing data binding
     * It's better to be private here. Otherwise, the code prompt in xml will have two age s (1, direct reference of member variables 2, code template generation). Although both can be used, OCD can't bear it.
     */
    private int age;

    @Bindable
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
        notifyPropertyChanged(BR.age);
    }

    /**
     * ObservableFields Implementing data binding (recommendation)
     */
    public final ObservableField<String> name = new ObservableField<>();


}

1.ObservableObject

A class that implements a visible interface that allows you to bind listeners and objects to listen for changes in all attributes of that object.

It can be seen that interfaces have mechanisms for adding and deleting listeners, but the notification mechanism for data changes depends on the developer. To make the development more concise, create a base class (BaseObservable) to implement the listener registration mechanism. When attributes change, the data implementation class is still responsible for the notification mechanism, in which the getter method is annotated with @Bindable, and the setter method is used to inform the attributes of the change.
During compilation, the binding annotation generates an entry in the BR class file, and then generates the BR class file in the module package. If the base class of the data class is immutable, the visible interface can be implemented in a way that facilitates Property Change Registry to effectively store and notify listeners.

In addition to this, we can also implement it through Observable Fields:

2.ObservableFields

In creating visible classes, developers can use visible fields when they want to save time or have many attributes, such as ObservableBoolean, ObservableByte, ObservableChar, ObservableShort, ObservableInt, ObservableLong, ObservableFloat, ObservableDouble, and ObservableParcelable.

Visible fields are visible objects with separate fields. The original version avoids boxing and unboxing in access operations. For ease of use, create fields in the data class that are modified with public final.

private static class User {
   public final ObservableField<String> firstName =
       new ObservableField<>();
   public final ObservableField<String> lastName =
       new ObservableField<>();
   public final ObservableInt age = new ObservableInt();
}

//Getting values through setter and getter methods
user.firstName.set("Google");
int age = user.age.get();

3. Observable Collections (Visible Collections)

Some applications use more dynamic structures to store data. Visible collections support keyed access to these data objects. When the key is a reference type like a string, you can use Observable ArrayMap.

4. All code integration:

1.Student class

//First inherit the BaseObservale class
public class Student extends BaseObservable{

    public Student(String name, int age) {
        this.name.set(name);
        this.age = age;
    }

    /**
     * ObservableObject Implementing data binding
     * It's better to be private here. Otherwise, the code prompt in xml will have two age s (1, direct reference of member variables 2, code template generation). Although both can be used, OCD can't bear it.
     */
    private int age;

    @Bindable
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
        notifyPropertyChanged(BR.age);
    }

    /**
     * ObservableFields Implementing data binding
     */
    public final ObservableField<String> name = new ObservableField<>();

}

2. In Activity:

 public class DataBindingBaseActivity extends AppCompatActivity {

     private ActivityDataBindingBaseBinding binding;

     private Student student;

     private List<String> contents = new ArrayList<>();

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

     private void inject() {
         binding = DataBindingUtil.setContentView(this, R.layout.activity_data_binding_base);

         student = new Student("qingMei2", 12);
         contents.add("content -> 0");
         contents.add("content -> 1");

 //        There are two ways to assign student s to XML
 //        binding.setStudent(student);
         binding.setVariable(BR.student, student);
         binding.setPresenter(new Presenter());
         binding.setContents(contents);
     }

     public class Presenter {

         public void onNameClick(String Name) {
             Toast.makeText(DataBindingBaseActivity.this, Name, Toast.LENGTH_SHORT).show();
         }

         public void onAgeClick(int age) {
             Toast.makeText(DataBindingBaseActivity.this, String.valueOf(age), Toast.LENGTH_SHORT).show();
         }

         /**
           * Listener and object binding, see Student class for details
           * {@link Student}
           */
          //Implementing Dynamic Updating of Data by ObserableObject
          public void onAgeAdd3() {
              student.setAge(student.getAge() + 3);
          }

          //Observable Field implements dynamic update of data (clearer)
          public void onNameAppendPoint() {
              student.name.set(student.name.get() + ".");
          }

          // Observable Collections
          public ObservableArrayMap<String, Object> datas = new ObservableArrayMap<>();

          {
              datas.put("string", "I am a string");
              datas.put("int", 1000);
              datas.put("student", student);
          }
      }

 }

3. layout file

 <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:bind="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <!--DataBinding Support AutoComplete,therefore type Fill Student,You can do the same as in the code.-->
        <!--The user variable within data describes a property that may be used within this layout.-->
        <variable
            name="student"
            type="com.mei_husky.samplemvvm.model.Student" />

        <variable
            name="presenter"
            type="com.mei_husky.samplemvvm.view.activity.DataBindingBaseActivity.Presenter" />

        <!--Generic support will be redlined at compile time, but it can run directly,But you need to escape characters, such as:&lt;perhaps&gt;-->
        <variable
            name="contents"
            type="List&lt;String&gt;" />

        <!--wrong lang Classes of packages need to be import(For example, the tool class, and then you can use its methods-->
        <import
            alias="Utils"
            type="com.mei_husky.samplemvvm.util.MyUtils" />

        <import type="java.util.List" />

    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="com.mei_husky.samplemvvm.view.activity.DataBindingBaseActivity">

        <!--Fixed characters we can use `` Package, corresponding, external""package-->
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{() -> presenter.onNameClick(student.name)}"
            android:text="@{`Student FirstName :` + student.name}"
            android:textAllCaps="false"
            android:textSize="14sp" />

        <!--Use MyUtils Tool class(alias Utils),Return in callback method age+1 -->
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{() ->  presenter.onAgeClick(Utils.addAge(student.age))}"
            android:text="@{`Student Age :` + student.age}"
            android:textAllCaps="false"
            android:textSize="14sp" />

        <!--Through namespaces bind include Correspondence variable Objects-->
        <include
            layout="@layout/include_bind_views"
            bind:studentInner="@{student}" />

        <!--Fixed characters can also be used "" Package, corresponding, external``package-->
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:text='@{"list :"+contents[0]}' />

        <!--Reference resource files-->
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:text="@{`Reference resource files appName -> ` + @string/app_name}" />

        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{() ->  presenter.onAgeAdd3()}"
            android:text="@{`ObservableObjects binding age+3`}"
            android:textAllCaps="false"
            android:textSize="14sp" />

        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{() ->  presenter.onNameAppendPoint()}"
            android:text="@{`ObservableField binding name + 「.」`}"
            android:textAllCaps="false"
            android:textSize="14sp" />

        <!--ObservableMap-->
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:text="@{`ObservableMap,String =` + presenter.datas[`string`]}" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:text="@{`ObservableMap,int =` + presenter.datas[`int`]}" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:text="@{`ObservableMap,Student =` + presenter.datas[`student`]}" />

    </LinearLayout>
</layout>

The effect is as follows:

V. Summary:

This article is a summary of the study of Data Binding. It has more content, but it is well understood.

Reference documents:

Data Binding Google Official Documents
Users who have not turned over the wall can read this:
Data Binding Guide: google Official Document Translation

All the code in this article is hosted on GitHub:

Github portal, click on me to see the source code

This demo contains all the code in this article, but is not limited to:

DemoApp built with Mvvm + Data Binding

1.MainActivity

- Initial Use and Boot Interface of Data Binding

2.DataBindingBaseActivity

- All the basic usage methods of Databinding

3.RecyclerBindActivity

- RecyclerView uses data Binding for list presentation

4.MulTypeRecyclerBindActivity

- Using data Binding in RecyclerView to display multiple types of lists

Topics: Android xml Google Java