https://developer.android.com/topic/libraries/data-binding/two-way
Be careful not to introduce infinite loops when using two-way data binding. When the user changes an attribute, the method annotated using @InverseBindingAdapter
is called, and the value is assigned to the backing property. This, in turn, would call the method annotated using @BindingAdapter
, which would trigger another call to the method annotated using @InverseBindingAdapter
, and so on.
For this reason, it’s important to break possible infinite loops by comparing new and old values in the methods annotated using @BindingAdapter
.
ensure that the view’s attribute is changed before update it if it’s in two way binding
graph TB
subgraph DataFlow
UserAction/Lifecycle-->LoadData
end
subgraph TwoWayDataBinding
Attribute-->|Change by user,and call method|AttrChanged("method annotated with BindingAdapter-"app:xxxAttrChanged"")
-->|InverseBindingListener.onChange|DataBindingSystem("DataBindingSystem knows attribute has changed")
-->|Call annotated InverseBindingAdapter, assign value to|BackingProperty("BackingProperty:LiveData")
-->|Call annotated BindingAdapter, may change attribute|Attribute
LoadData-->|Resource<>|BackingProperty
end
@BindingAdapter(value = {"android:beforeTextChanged", "android:onTextChanged",
"android:afterTextChanged", "android:textAttrChanged"}, requireAll = false)
public static void setTextWatcher(TextView view, final BeforeTextChanged before,
final OnTextChanged on, final AfterTextChanged after,
final InverseBindingListener textAttrChanged) {
final TextWatcher newValue;
if (before == null && after == null && on == null && textAttrChanged == null) {
newValue = null;
} else {
newValue = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
if (before != null) {
before.beforeTextChanged(s, start, count, after);
}
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (on != null) {
on.onTextChanged(s, start, before, count);
}
if (textAttrChanged != null) {
textAttrChanged.onChange();
}
}
@InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged")
public static String getTextString(TextView view) {
return view.getText().toString();
}
@BindingAdapter("android:text")
public static void setText(TextView view, CharSequence text) {
final CharSequence oldText = view.getText();
if (text == oldText || (text == null && oldText.length() == 0)) {
return;
}
if (text instanceof Spanned) {
if (text.equals(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.setText(text);
}