1. Two-way binding
  2. Remaining annotations from DataBinding

Soon enough, we come to canto III of DataBinding. Here are some of the DataBinding advanced knowledge points (think you are advanced).

1. Bidirectional binding

We should have introduced bidirectional binding in the introduction of binding expressions, but for the sake of bidirectional binding completeness, we left it here. Bidirectional binding looks like this:

We can one-way bind data and some listeners (events). When the data is updated, the bound control is updated, and when the listener changes, the bound data is updated.

This is how bidirectional binding works.

DataBinding gives us the basic implementation and customization of bidirectional binding.

1.1 Types of observable data

Not all data types can be bidirectionally bound, DataBinding gives us some:It’s about this much.

Expressions that use bidirectional binding are also slightly different from regular binding expressions:

@={} This is a bidirectional binding expression, with one more equal sign = than a regular binding expression.

The most common use of bidirectional expressions is the EditText control, which updates the bound data as content is entered, but updates the display of the EditText control as data is changed.

1.2. Bidirectional binding feature

Not all attributes support bidirectional binding. There is a table that lists which attributes can be bidirectional binding:

1.3. Customizing observable Data Types (@bindable)

If none of the observable data types provided by DataBinding are suitable for your business, you can still customize them. Create a new class that inherits BaseObservable, create a property, annotate it with the @bindable annotation, and override the property set method, something like this:

class Demo : BaseObservable() {
    @Bindable
    var name: String = ""
        set(value) {
            field = value
            notifyPropertyChanged(BR.name)
        }
}
Copy the code

@bindable annotation: This annotation mainly applies to the GET method or the field itself and generates a BR value for the notifyPropertyChanged method to call to update the data.

We can now bind the name property bidirectionally:

< EditText android: text = "@ = {demo. Name}" / >Copy the code

But if you actually run it you’ll find that it’s reporting an error, and the reason it’s reporting an error is because of a type mismatch. What to do at this point?

1.4 Two-way Data Conversion (@inversemethod)

Databinding provides data conversion solutions, one-way and two-way, two-way first.

Define a static class that provides two methods. One acts as a forward data converter and the other as a reverse data converter, something like this:

object Canversion{
	@InverseMethod("chToString")
	fun stringToCh(value: String): CharSequence {
  		  return value
	}

	fun chToString(value: CharSequence): String {
  		  return value.toString()
	}
}
Copy the code

In fact, two top-level functions are ok.

InverseMethod annotation: This annotation modifies the forward converter, and InverseMethod takes a parameter, the name of the reverse data converter.

Remember the

tag that, once the converter is defined, allows us to import the class into THE XML using the forward converter:

<import type="... Conversion/">

<EditText
	android:text="@={Conversion. StringToCh (demo. Name)} "/>
Copy the code

1.5. Custom InverseBindingAdapter

If DataBinding’s default bidirectional binding feature does not meet your requirements, you can only customize it. In fact, the bidirectional binding consists of just two one-way bindings (data binding and listener binding), so the first step is to create two one-way bindings. Let’s re-implement bidirectional binding for Android :text. First create a data binding:

@BindingAdapter("app:test")
fun test(view: TextView, text: CharSequence?). {
    val oldText = view.text
    if (text == oldText || (oldText.isNullOrEmpty())) {
        return
    }
    if (text is Spanned) {
        if (text == oldText) {
            return; // No change in the spans, so don't set anything.}}else if(! haveContentsChanged(text, oldText)) {return; // No content changes, so don't set anything.
    }
    view.text = text
}
Copy the code

Data needs to be evaluated before assignment: only updated data can be assigned. Prevent infinite loops from happening.

Then, create an event listening binding:

@BindingAdapter("app:testAttrChanged")
fun setTextWatcher(view: TextView, textAttrChanged: InverseBindingListener?). {
    val newValue: TextWatcher? = if (textAttrChanged == null) {
        null
    } else {
        object : TextWatcher {
            //code..
            override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
                textAttrChanged.onChange()
            }
            //code..}}if(newValue ! =null) {
        view.addTextChangedListener(newValue)
    }
}
Copy the code

InverseBindingListener this is an event listener that notifies the EditText when it changes via the InverseBindingListener object.

Finally, associate the two bindings together:

@InverseBindingAdapter(attribute = "app:test", event = "app:testAttrChanged")
fun getTextString(view: TextView): CharSequence {
    return view.text // Returns the value of the control
}
Copy the code

InverseBindingAdapter annotation: This annotation has two parameters: Attribute specifies the name of the data binding and event specifies the name of the event binding. The function is to combine the two to form a bidirectional binding and to respond to an event binding by returning the value of the control when an event occurs.

With these three steps, the custom bidirectional binding properties are complete:

<EditText
	app:test="@={demo. Name} "/>
Copy the code

One detail of custom bidirectional binding: In custom bidirectional binding, app:testAttrChanged is recognized as follows: data binding name +AttrChanged. Therefore, the event can be omitted during association.

1.6. InverseBindingMethods and InverseBindingMethod

Is there an easier way to implement bidirectional binding? The answer is no!! A two-way binding must be a data binding plus an event binding. You can never get rid of that. However, if you have more than one bidirectional binding to implement, the only thing that can be simplified is the method decorated with the InverseBindingAdapter annotation.

Start by defining a class:

@InverseBindingMethods(
    InverseBindingMethod(
        type = TextView::class,
        attribute = "app:test",
        method = "getText"
    )
)
object BindingAdapterImpl
Copy the code

InverseBindingMethods: This annotation takes an array of InverseBindingMethod parameters. InverseBindingAdapter InverseBindingAdapter InverseBindingAdapter InverseBindingAdapter InverseBindingAdapter

InverseBindingMethods and InverseBindingMethod are batch inversebindingAdapters.

InverseBindingMethod This annotation takes four parameters:

Type: is a class object that specifies a bidirectionally bound control, such as TextView

Attribute: Specifies the data binding method like the InverseBindingAdapter parameter of the same name.

Event: Specifies the event binding method, as the InverseBindingAdapter parameter of the same name. (As you can see, I did not write this attribute in the above example because it can be inferred automatically.)

Method: Specifies the method by which the control provides data. (In fact, this parameter can be left unwritten if the control has a get method with the same name as the attribute, that is, if there is a getTest method in the TextView, this parameter can be default.)

Note that these two annotations replace the InverseBindingAdapter, so data binding and event binding still need to be implemented.

In a nutshell: When the observable data object changes, the object of type is updated through the method specified by the attribute. When the event event occurs, the data obtained from the method method is returned to the observable data object.

DataBinding the rest of the annotations

Bidirectional binding is finally over, and the annotations related to bidirectional binding are also covered in the explanation of bidirectional binding. Next, we will talk about the remaining annotations of DataBinding.

2.1 One-way Data Conversion

There are two types of data conversion mentioned above. Two-way data conversion has been introduced. Now let’s look at one-way data conversion:

@BindingConversion
fun dateToString(time: Date): String{
    return SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(time)
}
Copy the code

Convert Date to String output.

<TextView
	  android:text="@ {date}" />Date is an object of type dateCopy the code

Note: One-way data conversion only requires input (parameter list) output (return value) pair to be used automatically, no manual call required.

Note again: all data conversions are one-to-one only.

2.2 simplified one-way binding (BindingMethods and BindingMethod)

@BindingMethods({
        @BindingMethod(type = TextView.class, attribute = "android:autoLink", method = "setAutoLinkMask")
})
Copy the code

Just like bidirectional binding, BindingMethods receive an array of BindingMethods. BindingMethod = BindingMethod

The @bindingmethod annotation has three variables: type: specifies the control for the one-way binding, such as TextView attribute: specifies the name of the one-way BindingMethod: specifies the set method that responds to the one-way binding

BindingMethods and BindingMethods are equivalent to batch BindingAdapters.

The attribute attribute implements one-way binding, using the method method to set values to controls of type type.

conclusion

The last article covered dataBinding’s bidirectional binding and the rest of the annotations, so dataBinding is basically done. DataBinding is divided into three main parts: the XML part, the Activity/Fragment part, and the custom binding (one-way binding and two-way binding) part. You’ll see later that XML can use binding expressions because of the @BindingAdapter tag. Of course, this is just the use of DataBinding, the real implementation: the principle behind what makes @BindingAdapter so amazing is really at the heart of DataBinding. That’s all for the notes section. Learning these annotations from DataBinding is sufficient for daily development.

For learning, the most important thing is to write, only really write, realize, can be considered to master.

Because truth is in practice.