I. Basic introduction

DataBinding is an Android implementation of the MVVM pattern, designed to reduce the coupling between layout and logic and make code logic clearer. MVVM replaces the Presenter layer with the ViewModel layer as opposed to MVP. DataBinding can eliminate the findViewById() step we’ve been doing, significantly reduce the code in the Activity, bind data in one or both directions to the Layout file, help prevent memory leaks, and automatically detect null Pointers to avoid null pointer exceptions

To enable DataBinding, add the following code to the build.gradle file of the corresponding Model. After synchronization, support for DataBinding is introduced

android { dataBinding { enabled = true } }

1. Use DataBinding for page layout changes

<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"> <data> <import type="com.liquid.jetpackapp.data.Goods" /> <variable name="goods" type="Goods" /> </data> <! - the view root bureau - > <. Android support. The constraint. ConstraintLayout android: layout_width = "match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> ... </android.support.constraint.ConstraintLayout> </layout>Copy the code

The difference between the MVVM ViewModel and the original layout is that there is a layout tag which wraps the original layout. The data tag is used to declare the variables to be used and the types of variables. To implement the MVVM ViewModel, the data (Model) and UI (View) need to be bound. The Data tag acts as a bridge between the View and the Model

2. To create a model

public class Goods{ @Bindable public String name; private String details; . }Copy the code

3. In the data TAB, declare the name of the variable to be used and the full path of the class

<data>
   <import type="com.liquid.jetpackapp.data.Goods" />
     <variable   
        name="goods"
        type="Goods" />
</data>
Copy the code

4. Here we declare a variable Goods of the Goods type. What we need to do is hook this variable with two TextView controls and make TextView display corresponding text by setting the variable value of Goods

<TextView
    android:id="@+id/tv_name"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    android:text="@{goods.name}"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

<TextView
    android:id="@+id/tv_details"
    app:layout_constraintLeft_toRightOf="@+id/tv_name"
    app:layout_constraintTop_toTopOf="parent"
    android:text="@{goods.details}"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />
Copy the code

TextView is referenced to the associated variable by @{goods.name}, and DataBinding maps it to the corresponding getter method

5. You can then set the layout file in the Activity via DataBindingUtil, omitting the setContentView() method of the original Activity, and assigning the goods variable

private Goods goods;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
    goods = new Goods("goods","food",34);
    activityMainBinding.setUserInfo(goods);
}
Copy the code

6. @{goods.name} has no explicit value in the layout file, so it will not be displayed in the preview view, so it is not easy to see the size of the text and the color of the font. In this case, you can set the default value to the @{goods.name}. The default value cannot contain quotation marks

android:text="@{goods.name,default=food}"
Copy the code

Alternatively, the control with the specified ID can be obtained directly via the ActivityMainBinding

activityMainBinding.tvName.setText("food");
Copy the code

7.Databinding can also be used in Fragment and RecyclerView. For example, you can see the use of Databinding in fragments

@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    FragmentBlankBinding fragmentBlankBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_blank,container, false);
    return fragmentBlankBinding.getRoot();
}
Copy the code

2. One-way binding

There are three ways to automatically drive UI refresh for data changes: BaseObservable, ObservableField, and ObservableCollection

When a pure ViewModel class is updated, it does not automatically update the UI. With data binding, we naturally expect the UI to refresh immediately after data changes. An Observable is the concept behind this

BaseObservable provides notifyChange(), which refreshes all fields, and notifyPropertyChanged(), which updates only the flag of the corresponding BR, which is generated by annotation

An @bindable view that can be associated with the BR notify specific property

Public class Goods extends BaseObservable {// Public class Goods extends BaseObservable {// Public class Goods extends BaseObservable {// Public class Goods extends BaseObservable {// Public class Goods extends BaseObservable {// Public class Goods extends BaseObservable {// Public class Goods extends BaseObservable {// Public class Goods extends BaseObservable { // If it is a private modifier, add the @bindable annotation to the member variable's get method. public void setName(String name) { this.name = name; notifyPropertyChanged(BR.name); } @Bindable public String getDetails() { return details; } public void setDetails(String details) { this.details = details; notifyChange(); }}Copy the code

In the setName() method, only this field is updated, whereas in the setDetails() method, all fields are updated, and when values are updated, the corresponding UI display is updated with them

2. Realized the observables interface class allowed to register a listener, when the property changes of the observed object will notify the listeners, at this point you need to use OnPropertyChangedCallback propertyId is used to identify a particular field

goods.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() { @Override public void onPropertyChanged(Observable sender, int propertyId) { switch (propertyId) { case BR.details: Log.e("MainActivity","details"); break; case BR.name: Log.e(""MainActivity","name"); break; case BR.price: Log.e(""MainActivity","price"); break; }}});Copy the code

Used to observe changes in the value of attributes in goods

3.ObservableField

Inherits from an Observable class is relatively restrictive and also requires notify, so use ObservableField for simplicity. ObservableField can be understood as the official encapsulation of field annotation and refresh operations in BaseObservable. The official native provides the encapsulation of basic data types. For example, ObservableBoolean, ObservableByte, ObservableChar,

ObservableShort, ObservableInt, ObservableLong, ObservableFloat, ObservableDouble, and ObservableParcelable, Other types can also be declared through the ObservableField generics

public class Goods { private ObservableField<String> name; private ObservableFloat price; private ObservableField<String> details; public ObservableGoods(String name, float price, String details) { this.name = new ObservableField<>(name); this.price = new ObservableFloat(price); this.details = new ObservableField<>(details); } ` ` `}Copy the code

Changes to the value of the ObservableGoods property trigger an immediate UI refresh, conceptually indistinguishable from an inherited BaseObservable

4.ObservableCollection

DataBinding also provides wrapper classes to replace the native List and Map, ObservableList and ObservableMap, respectively, which refresh the bound view as the data they contain changes

<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"> <data> <import type="androidx.databinding.ObservableList" /> <import type="androidx.databinding.ObservableMap" /> <variable name="list" type="ObservableList&lt; String&gt;" /> <variable name="map" type="ObservableMap&lt; String,String&gt;" /> <variable name="index" type="int" /> <variable name="key" type="String" /> </data> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/tv_list" app:layout_constraintTop_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" android:text="@{list[index],default=xx}" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/tv_map" app:layout_constraintTop_toBottomOf="@+id/tv_list" app:layout_constraintStart_toStartOf="parent" android:text="@{map[key],default=gg}" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </androidx.constraintlayout.widget.ConstraintLayout> </layout> public class MainActivity extends AppCompatActivity { private ObservableList<String> list; private ObservableMap map; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this,R.layout.activity_main); list = new ObservableArrayList(); list.add("Ye"); list.add("leavesC"); activityMainBinding.setList(list); activityMainBinding.setIndex(1); map = new ObservableArrayMap(); map.put("name","lvdouwan"); map.put("age","24"); activityMainBinding.setMap(map); activityMainBinding.setKey("name"); }}Copy the code

Two-way data binding Two-way data binding means that when data changes, the view can be refreshed, and when the view changes, the data can also be changed

Android :text=”@={goods.name}” android:text=”@={goods.name}” android:text=”@={goods.name}”

<layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <data> <import type="com.liquid.jetpackapp.data.Goods"/> <variable name="goods" type="ObservableGoods" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <EditText ··· Android :text="@={goods.name}" /> <EditText ··· Android :text="@={goods.name}"  /> </LinearLayout> </layout> public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main); Goods goods = new Goods("code", "hi", 23); activityMainBinding.setGoods(goods); }}Copy the code

Event binding is technically a variable binding, except that the variable is set to the callback interface. Event binding can be used for many of the following callback events

android:onClick
android:onLongClick
android:afterTextChanged
android:onTextChanged
Copy the code

Create a new GoodsHandler class inside the Activity to declare the corresponding callback methods for onClick() and afterTextChanged() events

public class GoodsHandler{ public void changeGoodsName() { goods.setName("code " + new Random().nextInt(100)); goods.setPrice(new Random().nextInt(100)); } public void changeGoodsDetails() { goods.setDetails("hi " + new Random().nextInt(100)); goods.setPrice(new Random().nextInt(100)); } public void afterTextChanged(Editable s) { } public void afterUserPassword(Editable s){ } } <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"> <data> <variable name="goodsHandler" type="com.liquid.jetpackapp.MainActivity.GoodsHandler" /> </data> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <Button android:id="@+id/btn_one" app:layout_constraintTop_toTopOf="parent" app:layout_constraintLeft_toLeftOf="parent" Android: text = "change the attribute name and price," the android: onClick = "@ {() - > goodsHandler. ChangeGoodsName ()}" android: textAllCaps = "false" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/btn_two" App :layout_constraintTop_toBottomOf="@+id/btn_one" app:layout_constraintLeft_toLeftOf="parent" Android :text=" Change attributes The details and price, "the android: onClick =" @ {() - > goodsHandler. ChangeGoodsDetails ()} "android: textAllCaps =" false" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </androidx.constraintlayout.widget.ConstraintLayout> </layout> public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this,R.layout.activity_main); activityMainBinding.setGoodsHandler(new GoodsHandler()); } public class GoodsHandler{ public void changeGoodsName() { goods.setName("code " + new Random().nextInt(100)); goods.setPrice(new Random().nextInt(100)); } public void changeGoodsDetails() { goods.setDetails("hi " + new Random().nextInt(100)); goods.setPrice(new Random().nextInt(100)); } public void afterTextChanged(Editable s) { } public void afterUserPassword(Editable s){ } } }Copy the code