本文会分析InverseBindingAdapter并介绍其应用场景

一、InverseBindingAdapter 和 BindingAdapter

BindingAdapter主要定义赋值给一个view,反过来,InverseBindingAdapter定义从一个view取值

二、InverseBindingAdapter的使用

TextView的android:text=”@={vm.data}”可以把textview同viewmodel双向绑定,这是在默认的TextViewBindingAdapter实现的:

public class TextViewBindingAdapter {

    private static final String TAG = "TextViewBindingAdapters";
    @SuppressWarnings("unused")
    public static final int INTEGER = 0x01;
    public static final int SIGNED = 0x03;
    public static final int DECIMAL = 0x05;

    @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);
    }

    @InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged")
    public static String getTextString(TextView view) {
        return view.getText().toString();
    }
  
  ...
    
    @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();
                    }
                }

                @Override
                public void afterTextChanged(Editable s) {
                    if (after != null) {
                        after.afterTextChanged(s);
                    }
                }
            };
        }
        final TextWatcher oldValue = ListenerUtil.trackListener(view, newValue, R.id.textWatcher);
        if (oldValue != null) {
            view.removeTextChangedListener(oldValue);
        }
        if (newValue != null) {
            view.addTextChangedListener(newValue);
        }
    }

这里的关键点就是@InverseBindingAdapter去定义取值操作,注意这里有个event = “android:textAttrChanged”,这指的是当这个事件android:textAttrChanged发生的时候才去从view取值到viewmodel。那么如何定义android:textAttrChanged何时发生呢,这里是个纯粹的观察者,TextView的话是需要定义一个TextWatcher然后在onTextChanged的时候回调:textAttrChanged.onChange()。

这里还有一个实例,就是为一个自定的控件定义InverseBindingAdapter:

public final class PlusMinusBindingAdapter {

    private PlusMinusBindingAdapter() {
    }

    @BindingAdapter("android:count")
    public static void setCount(PlusMinusLayout view, int count) {
        view.setCount(count);
    }

    @InverseBindingAdapter(attribute = "android:count", event = "countChanged")
    public static int getCount(PlusMinusLayout view) {
        return view.getCount();
    }

    @BindingAdapter(value = {"countChanged"}, requireAll = false)
    public static void setDelegate(PlusMinusLayout view, final InverseBindingListener listener) {
        final PlusMinusLayout.Delegate delegate;
        if (listener == null) {
            delegate = null;
        } else {
            delegate = new PlusMinusLayout.Delegate() {
                @Override
                public void onCountChanged(int count, boolean isPlus) {
                    if (listener != null) {
                        listener.onChange();
                    }
                }
            };
        }
        view.setDelegate(delegate);
    }
}

跟前面讲解的套路是一样的,这里就不多说了。