Detailed explanation and use of one-way and two-way data binding of Android databinding (3)

1. Introduction

        Through the previous two documents, we have a general understanding of the working method of databinding, the initialization of view, and the use of recycleview. But these UIs are inseparable from data filling and data modification.

When it comes to data binding, many developers often hear that the data binding of databinding is simple, one-way binding, and two-way binding in their work, which is unpredictable and dare not start. Some novices even give up after listening to it. Next, I will explain the data binding and use of databinding through code, including map, list, and user-defined classes, so that complex events can be simplified, and everyone can master and use them

data binding

        There are two types of data binding, one is supported by the system, and the other is the data of databind. Next, we will introduce the sub-beams

1. System default data type

Basic data

String、int、float、double,boolean

    <data class="MyDataInfo">

        <variable
            name="name"
            type="String" />

        <variable
            name="age"
            type="int" />

        <variable
            name="bodyH"
            type="float" />

        <variable
            name="income"
            type="double" />

        <variable
            name="sex"
            type="boolean" />
    </data>

Simple type, we can use it directly

 Layout final reference:


        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{name}" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(age)}" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(bodyH)}" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(income)}" />
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(sex)}" />

Notice:

Any value in the layout needs to be processed into a string type, that is to say, boolean or double cannot directly @{double}@{boolean}, this is wrong

correct:

   <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(sex)}" />

mistake:

   <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{sex}" />

Note: Some static methods can be directly referenced in the layout, which will be introduced separately later

2. Aggregate data binding such as ArrayList and HashMap

The data of the aggregation column is also supported under the data. There are three commonly used ones: map, list, and sparseArry.

How to use it? Because the data under data must specify generics, all three types of data support generics, so you must specify generics.

Generic format:

<variable
    name="list"
    type="ArrayList&lt;String&gt;" />

Specify the generic type in type. type format=ArrayList<String>

Some people will not understand what < and > mean,

Normal format: ArrayList<String>

databind:ArrayList&lt;String&gt;

So (< is <) left parenthesis, (> is >) right parenthesis

    <data class="MyDataInfo">

        <import type="java.util.HashMap" />
        <import type="java.util.ArrayList" />
        <import type="android.util.SparseArray"/>

        <variable
            name="key"
            type="String" />

        <variable
            name="index"
            type="int" />

        <variable
            name="map"
            type="HashMap&lt;String,Object&gt;" />

        <variable
            name="list"
            type="ArrayList&lt;String&gt;" />

        <variable
            name="arry"
            type="SparseArray&lt;String&gt;" />

    </data>

Here, it should be noted that it is best to set all keys and indexes dynamically, otherwise it will be inconvenient for business development

How to bind data data in view:

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{arry.get(index)}" />
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{list.get(index)}" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{map[key]}" />

Data settings:

        var map = HashMap<String, Any>()
        map.put("name", "map你好")
        databind.key = "name"
        databind.map = map
        var list = ArrayList<String>()
        list.add("list0index")
        list.add("list1index")
//        databind.list = list

        var spar=SparseArray<String>()
        spar.put(0,"0 value SparseArray")
        spar.put(1,"1 value SparseArray")
        spar.put(100,"100 value SparseArray")
        databind.arry=spar
        databind.index = 100

illustrate:

        If your list is not set, that is, it is null in databind, even if you set key or index, it will not be referenced. If you set data, index will cause the array to go out of bounds, but will not throw a null pointer

Two, databind data introduction

        What we have explained above is done through system data fetching, but when we use databinding, we want to use its data characteristics, one-way binding and two-way binding.

        The data binding we introduced above is not involved. Next, we will explain the one-way binding and two-way binding through the method provided by databind.

1. One-way binding

What is one-way binding:

One-way binding means that when the data changes, the UI will be automatically notified to refresh, but after the UI content changes, the data will not be referenced to change.

There are two methods of data binding, the first one is family bucket, the second one is user-defined

The first type: family bucket mode BaseObservable, Bindable

BaseObservable: Provides a data update mechanism, which can update the complete data through notifyPropertyChanged(int field) and notifyChange()

Bindable: generate associated fields to form an associated graph

how to use:

1. Inheritance: BaseObservable
2. Bind fields through Bindable annotations, and the changed fields must be public, otherwise they will be bound to the get method

kotlin:

Bind directly to the get method

class MySchool : BaseObservable() {

    @get:Bindable
    var name: String = ""
        set(value) {
            field = value
            notifyPropertyChanged(BR.name)
           

        }

}

Java:

public class MySchool extends BaseObservable {


    private String schoolName="";

    @Bindable
    public String getSchoolName() {
        return schoolName;
    }

    public void setSchoolName(String schoolName) {
        this.schoolName = schoolName;
        notifyPropertyChanged(BR.schoolName);
    }
}

About notifyPropertyChanged() and notifyChange()

notifyPropertyChanged: only refresh the specified field

notifyChange: Refresh all bindable fields under the object

Notice:

        Someone wrote the set method first, and BR could not find the specified field because the field has not been bound by the bindable annotation to generate the corresponding relationship diagram, so bindable must be updated first, otherwise the corresponding field cannot be found

Field update listener: addOnPropertyChangedCallback

Not only can one-way binding be used, but we can determine the fields corresponding to the currently bound data:

BaseObservable provides an addOnPropertyChangedCallback callback, where field monitoring can be set
        var detail = MySchool()
        detail.addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback(){
            override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
//                TODO("Not yet implemented")

            }
        })


2. Custom binding field ObservableField

Through the above, we already know the use of family buckets, but databinding also provides the basic binding method ObservableField,

BaseObservableField is the basic type, which can be used to specify generic types, and can also be bound by other data types provided 

ObservableFloat 
ObservableBoolean ObservableInt 
ObservableParcelable 
ObservableChar 
aggregated 
data 
ObservableArrayMap 
ObservableArrayList

Next, let's start with the most basic BaseObservableField foundation

data source:

class BaseFieldData {

    lateinit var name: ObservableField<String>
    lateinit var age: ObservableField<Int>
    lateinit var god: ObservableField<Dog>


}

class Dog() : Parcelable {

    lateinit var name: String

    lateinit var hostName: String

    constructor(parcel: Parcel) : this() {
        name = parcel.readString()!!
        hostName = parcel.readString()!!
    }

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeString(name)
        parcel.writeString(hostName)
    }

    override fun describeContents(): Int {
        return 0
    }

    companion object CREATOR : Parcelable.Creator<Dog> {
        override fun createFromParcel(parcel: Parcel): Dog {
            return Dog(parcel)
        }

        override fun newArray(size: Int): Array<Dog?> {
            return arrayOfNulls(size)
        }
    }


}

accomplish:

    fun initData() {
        var name = ObservableField<String>("我的名字")
        var age = ObservableField<Int>(100)
        var dog = Dog()
        dog.name = "小黑"
        dog.hostName = name.get()!!;
        var isDog = ObservableField<Dog>(dog)
        var baseData = BaseFieldData()
        baseData.age = age
        baseData.god = isDog
        baseData.name = name
        dataBind.data = baseData


        dataBind.testClick.setOnClickListener {
            name.set("新名字")
            var newDog=Dog()
            newDog.name="我是小白"
            newDog.hostName=name.get().toString()
            isDog.set(newDog)
        }


    }

In this way, we have finished worrying about binding, every time we only need to update the variable values ​​​​of name and age, the UI will automatically refresh

Each field sets generic parameters through set(), and returns through get.

Why did we not manually update, the system can automatically update the UI?

We can know by looking at the set() of ObservableField

public void set(T value) {
    if (value != mValue) {
        mValue = value;
        notifyChange();
    }
}

When we call set(), the global refresh is also called by default.

The second type: packaged with databind

analyze:

        The encapsulated ObservableChar has inherited BaseObservableField

public class ObservableChar extends BaseObservableField implements Parcelable, Serializable

So just specify the content type in the constructor class, so that we no longer need to set generic parameters when using it.

var size=ObservableDouble(12.0)
      <import type="androidx.databinding.ObservableDouble"/>

        <variable
            name="size"
            type="ObservableDouble" />

     <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(size)}" />
       dataBind.testClick.setOnClickListener {
        
            size.set(123.2)
        }

These are all easy to handle. Next we will introduce ObservableArrayList and ObservableArrayMap

3. Data collection:

ObservableArrayList和ObservableArrayMap

 Introducing an ObservableArrayMap:

data in the layout:


        <import type="androidx.databinding.ObservableArrayMap" />

        <variable
            name="map"
            type="ObservableArrayMap&lt;String,String&gt;" />

        <variable
            name="key"
            type="String" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{map.get(key)}" />

Implemented in the code:

        var map = ObservableArrayMap<String, String>()
        map.put("name", "zhangshan")
        dataBind.map = map
        dataBind.key = "name"


      dataBind.testClick.setOnClickListener {
     
            map.put("name", "修改过的")
        }
 
 

Similarly, the put method of map is as follows:

public V put(K k, V v) {
    V val = super.put(k, v);
    notifyChange(k);
    return v;
}

The global refresh is also called. Similarly, the add method of ArryList is also like this:

After introducing the above usage, you will find that these seem to be one-way binding. What is two-way binding? it's actually really easy

Four, two-way binding

        After introducing the one-way binding, in fact, everyone has mastered the two-way binding. In Jay Chou's words, when it comes to global music, Chinese is the best.

Two-way binding is more than one-way binding when view binding, one more = sign

unidirectional:

<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@{dbValue}" />

Two-way:

<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@={dbValue}" />

used here

var name = ObservableField<String>("my name")

carry out testing.

renderings

 Summarize:

               As long as we master one-way binding, two-way binding will naturally be resolved. However, when using two-way, you need to pay attention. If there are multiple references in different scenarios, it will lead to data confusion. So, you need to be extra careful when using it.

        

Guess you like

Origin blog.csdn.net/qq36246172/article/details/128192081#comments_28082275