The original article was published on wechat official account: Jzman-blog

The use of ViewModel, LiveData, and Lifecycle architecture components has been summarized previously. Please read the following article for details:

  • Lifecycle for Android Jetpack components
  • LiveData for Android Jetpack
  • ViewModel for Android Jetpack components

This article focuses on the basic use of dataBinding. The main contents are as follows:

  1. DataBinding support
  2. Layout File Configuration
  3. Data binding
  4. Special expression
  5. event
  6. Custom binding classes
  7. Others

DataBinding support

To use dataBinding, you need to configure it in the build.gradle file under the App Module as follows:

// Set up support for dataBinding

dataBinding {

    enabled = true

}

Copy the code

Layout File Configuration

The Data Binding Library automatically generates the classes needed to bind the views and Data objects in the layout. The Layout file of the Data Binding Library uses the Layout tag as the root tag, followed by the specific Data elements and view elements. This view element is the location to bind the layout file, which is referenced as follows:


       

<! --dataBinding must use layout as the root tag

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <! -- Data object -->

    <data>

        <variable name="user" type="com.manu.databindsample.data.User"/>

    </data>

    <! -- View element -->

    <LinearLayout

        android:orientation="vertical"

        android:layout_width="match_parent"

        android:layout_height="match_parent">


        <! -- Set specific attribute values in dataBinding in @{} -->

        <TextView android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:text="@ {user name, the default name} ="/>


    </LinearLayout>

</layout>

Copy the code

Data entity

The name property in “@{user.name}” eventually maps to calling the getter method of the data object, that is, the getter method. Of course, if the data object has a corresponding name method, the method with the same name will be called when there is no corresponding getter method. If both are present, the corresponding getter method is called first, as follows:

/ * *

* Data entities

 * Powered by jzman.

 * Created on 2018/11/28 0028.

* /


public class User {

    private String name;



    public User(a) {

    }



    public User(String name) {

        this.name = name;

    }



    // Both have priority calls

    public String getName(a) {

        return name;

    }

    // The getter method is not called

    public String name(a) {

        return "name";

    }



    / /...

}

Copy the code

Data binding

DataBinding generates binding classes for each layout file. By default, the name of the class is based on the name of the layout file. If the layout file is named activity_main, the binding class for the layout file is ActivityMainBinding. This class contains all the binding of the data object to the layout file. How to bind the data and view is as follows in Activty:

public class MainActivity extends AppCompatActivity {



    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        // Generate the binding class

        ActivityMainBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_main);

        // Bind views and data

        User user = new User("Zhang");

        binding.setUser(user);

    }

}

Copy the code

Fragment bindings are as follows:

/ / method to inflate

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

                         Bundle savedInstanceState)
 
{

    FragmentOneBinding oneBinding = DataBindingUtil.inflate(inflater,R.layout.fragment_one,container,false);

    User user = new User("Xiao Ming");

    oneBinding.setUser(user);

    return oneBinding.getRoot();

}

/ / the bind method

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

    View view = inflater.inflate(R.layout.fragment_one,container,false)
;

    FragmentOneBinding oneBinding = FragmentOneBinding.bind(view);

    User user = new User("Xiao Ming");

    oneBinding.setUser(user);

    return view;

}

Copy the code

The binding of other layouts is basically done using the inflate and bind methods of a generated binding class.

Special expression

  • Ternary operator simplification
// Complete

android:text="@{user.displayName ! = null ? user.displayName : user.lastName}"

/ / short

android:text="@{user.displayName ?? user.lastName}"

Copy the code
  • Null pointer exception handling

The generated binding class automatically checks for null values to avoid NullPointerException. In the expression @{user.name}, if user is null, user.name is assigned its default value NULL. If user. Age is referenced, where age is of type int, the data binding uses the default value 0.

  • A collection of

       

<layout xmlns:android="http://schemas.android.com/apk/res/android">



    <data>



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



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



        <! --Map-->

        <variable

            name="map"

            type="Map< String,String>" />




        <variable

            name="key"

            type="String" />




        <! --List-->

        <variable

            name="list"

            type="List< String>" />




        <variable

            name="index"

            type="int" />


    </data>

    <! -- Notice the Map and List values -->

    <LinearLayout

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:orientation="vertical">




        <TextView

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:text="@{map.key}" />




        <TextView

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:text="@{list[0]}" />




    </LinearLayout>

</layout>

Copy the code

Map. key can be used in the @{} expression to obtain the value corresponding to the key in the Map set. Data of the List type can be directly evaluated by the index. In addition, < used in the variable tag should be escaped. And use < instead of <, otherwise error:

Error: and the element type"variable"The associated"type"Attribute value cannot contain'<'Characters.

Copy the code
  • Strings are used in @{} expressions

There are two ways to use strings instead of string variables in @{} expressions:

<! -- Use single quotes -->

<TextView

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:text='@{map["key"]}' />


<! -- Use trailing quotes -->

<TextView

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:text="@{map[`key`]}" />


<! String resources can be used in @{} -->

<TextView

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="@{@string/app_name}"/>


Copy the code

event

With databinding, event listeners can be set up as method references or listener bindings, the difference being that event listeners are created at databinding time, whereas event listeners are bound at event firing time.

  • Method references

Events can be tied directly to event handling methods, in contrast to the normal Android :onClick property, which is configured to be processed at compile time and receive a compile-time error if the method does not exist or is incorrectly signed. First create an event handler like this:

/ * *

 * Powered by jzman.

 * Created on 2018/11/30 0030.

* /


public class MyHandler {

    / * *

     * @param view

* /


    public void onClickEvent(View view){

        Toast.makeText(view.getContext(), "click me", Toast.LENGTH_SHORT).show();

    }

}

Copy the code

Then, configure specific events such as onClick in the corresponding layout file. Here, the onClick event is used as an example, as follows:


       

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable

            name="handler"

            type="com.manu.databindsample.handler.MyHandler"/>


    </data>



    <LinearLayout

        android:layout_width="match_parent"

        android:layout_height="match_parent"

        android:orientation="vertical">


        <! -- First way -->

        <Button

            android:layout_width="match_parent"

            android:layout_height="wrap_content"

            android:text="click me"

            android:onClick="@{handler::onClickEvent}"/>


        <! -- Second way -->

        <Button

            android:layout_width="match_parent"

            android:layout_height="wrap_content"

            android:text="click me"

            android:onClick="@{handler.onClickEvent}"/>


    </LinearLayout>

</layout>

Copy the code

Finally, set the data object handler in the corresponding Activity, see the following details:

@Override

protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    ActivityEventHandlerBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_event_handler);

    binding.setHandler(new MyHandler());

}

Copy the code

This binds successfully through method reference events.

  • To monitor the binding

In the event callback, first, create an event callback method as follows:

/ * *

* Listen binding

 * Powered by jzman.

 * Created on 2018/12/3 0003.

* /


public class MyPresenter {

    private Context mContext;



    public MyPresenter(Context mContext) {

        this.mContext = mContext;

    }



    public void onClickEvent(User user) {

        Toast.makeText(mContext, user.getName(), Toast.LENGTH_SHORT).show();

    }

}

Copy the code

Then, configure specific events such as onClick in the corresponding layout file. Here, the onClick event is used as an example, as follows:


       

<layout xmlns:android="http://schemas.android.com/apk/res/android">



    <data>

        <variable

            name="user"

            type="com.manu.databindsample.data.User" />




        <variable

            name="presenter"

            type="com.manu.databindsample.handler.MyPresenter" />


    </data>



    <LinearLayout

        android:layout_width="match_parent"

        android:layout_height="match_parent"

        android:orientation="vertical">




        <Button

            android:layout_width="match_parent"

            android:layout_height="wrap_content"

            android:onClick="@{() -> presenter.onClickEvent(user)}"

            android:text="click me 3" />


    </LinearLayout>

</layout>

Copy the code

In the corresponding Activity, you can set the data object presenter as follows:

@Override

protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    ActivityEventHandlerBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_event_handler);

    binding.setUser(new User("android"));

    binding.setPresenter(new MyPresenter(this));

}

Copy the code

When you call the event method in the XML above, you can configure the current View as follows:

<Button

    android:layout_width="match_parent"

    android:layout_height="wrap_content"

    android:onClick="@{(view) -> presenter.onClickEvent(user)}"

    android:text="click me 3" />


Copy the code

The corresponding event callback method is as follows:

public class MyPresenter {

    public void onClickEvent(View view, User user){}

}

Copy the code

Alternatively, you can use the ternary operator for event binding. Void can be used as the operator as follows:

android:onClick="@{(v) -> v.isVisible() ? presenter.doSomething() : void}"

Copy the code

Custom binding classes

By default, the name of the binding class is determined by the name of the layout file. To customize the binding class, you can specify the name of the binding class using the class attribute on the data tag of the layout file. You can also add the completed package path to the name of the custom class.

<! -- Custom binding class -->

<data class="com.manu.test.CustomBinding">

    <variable name="user" type="com.manu.databindsample.data.User"/>

</data>

Copy the code

Others

Use the import keyword in databinding to import related classes. Java.lang.* classes are automatically imported by default. Views with the same name can be distinguished by alias.

<import type="android.view.View"/>

<import type="com.manu.View"

        alias="MView"/>


Copy the code

Use the variable keyword to define the variables to be used in the XML layout. If you use the include layout, use bind. The include layout uses the same variables as the main layout. Create a layout test_layout.xml file that includes the following:


       

<layout xmlns:android="http://schemas.android.com/apk/res/android">



    <data>

        <variable

            name="userTest"

            type="com.manu.databindsample.data.User" />


    </data>



    <LinearLayout

        android:layout_width="match_parent"

        android:layout_height="match_parent">




        <TextView

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:text="@{`this is include content... `+userTest.getName(),default=user}" />


    </LinearLayout>

</layout>

Copy the code

This layout is then referenced in the main layout as follows:


       



<layout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:bind="http://schemas.android.com/apk/res-auto">




    <data>

        <variable

            name="user"

            type="com.manu.databindsample.data.User" />


    </data>



    <LinearLayout

        android:layout_width="match_parent"

        android:layout_height="match_parent">


         <! --bind bind variables -->

        <include

            layout="@layout/test_layout"

            bind:userTest="@{user}" />


    </LinearLayout>

</layout>

Copy the code

Bind binds variables of type User used in both layouts, making the variables used in both layouts the same variable. In addition, Databinding does not support merge tags. See Binding Adapters in the next section.