自定义组件的Android双向数据绑定?

时间:2017-09-07 14:47:06

标签: android custom-component android-databinding two-way-binding android-binding-adapter

我正在尝试关注this博文,试图让双向数据绑定适用于自定义组件(其中包含EditText的约束视图)。

我能够让两个标准的EditText组件与我的模型保持同步(两种方式),但是我在自定义组件中的更改流入我的模型时遇到了麻烦(尽管单向数据绑定有效) )。

我的模特:

public class Model extends BaseObservable {
    private String value;

    @Bindable
    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
        notifyPropertyChanged(company.com.databinding.BR.value);
    }

    public Model() {
        value = "Value";
    }
}

的活动:

@InverseBindingMethods({
        @InverseBindingMethod(
                type = CustomComponent.class,
                attribute = "value",
                method = "getValue")
})
public class MainActivity extends AppCompatActivity {
    @BindingAdapter("value")
    public static void setColor(CustomComponent view, String value) {
        if (!value.equals(view.getValue())) {
            view.setValue(value);
        }
    }

    @BindingAdapter(
            value = {"onValueChange", "valueAttrChanged"},
            requireAll = false
    )
    public static void setListeners(CustomComponent view,
                                    final ValueChangeListener onValueChangeListener,
                                    final InverseBindingListener inverseBindingListener) {
        ValueChangeListener newListener;
        if (inverseBindingListener == null) {
            newListener = onValueChangeListener;
        } else {
            newListener = new ValueChangeListener() {
                @Override
                public void onValueChange(CustomComponent view,
                                          String value) {
                    if (onValueChangeListener != null) {
                        onValueChangeListener.onValueChange(view,
                                value);
                    }
                    inverseBindingListener.onChange();
                }
            };
        }

        ValueChangeListener oldListener =
                ListenerUtil.trackListener(view, newListener,
                        R.id.textWatcher);

        if (oldListener != null) {
            view.removeListener(oldListener);
        }
        if (newListener != null) {
            view.addListener(newListener);
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.activity_main);
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        binding.setModel(new Model());
    }
}

自定义组件:

public class CustomComponent extends ConstraintLayout {
    private String value;
    private EditText txt;
    private TextWatcher textWatcher;
    ValueChangeListener listener;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
        if (txt != null) {
            txt.setText(value);
        }
    }

    public CustomComponent(Context context) {
        super(context);
        init(context);
    }

    public CustomComponent(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public CustomComponent(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs);
    }

    private void init(Context context) {

    }

    private void init(Context context, AttributeSet attrs) {
        View.inflate(context, R.layout.custom_component, this);
        txt = findViewById(R.id.txt_box);
        final CustomComponent self = this;
        textWatcher = new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void afterTextChanged(Editable editable) {
                if (listener != null) {
                    listener.onValueChange(self, editable.toString());
                }
            }
        };
        txt.addTextChangedListener(textWatcher);
    }

    public void addListener(ValueChangeListener listener) {
        this.listener = listener;
    }

    public void removeListener(ValueChangeListener listener) {
        this.listener = null;
    }
}

public interface ValueChangeListener {
    public void onValueChange(CustomComponent view, String value);
}

我觉得那篇文章中的“挂钩事件”部分完全超出了我的想法;我真的只需要一个简单的setter和getter用于组件,因此无法理解在BindingAdapter中做了什么。在所有这些中,我认为这是我根本无法得到的这条线:

ValueChangeListener oldListener =
            ListenerUtil.trackListener(view, newListener,
                    R.id.textWatcher);

演示于:https://github.com/indgov/data_binding

1 个答案:

答案 0 :(得分:1)

很抱歉,ListenerUtil令人困惑。这仅在您的组件支持多个侦听器时才有用。在这种情况下,您不能只设置一个新的侦听器,您必须删除旧的侦听器并添加新的侦听器。 ListenerUtil可以帮助您跟踪旧的侦听器,以便可以将其删除。在您的情况下,它可以简化:

@BindingAdapter(
        value = {"onValueChange", "valueAttrChanged"},
        requireAll = false
)
public static void setListeners(CustomComponent view,
                                final ValueChangeListener onValueChangeListener,
                                final InverseBindingListener inverseBindingListener) {
    ValueChangeListener newListener;
    if (inverseBindingListener == null) {
        newListener = onValueChangeListener;
    } else {
        newListener = new ValueChangeListener() {
            @Override
            public void onValueChange(CustomComponent view,
                                      String value) {
                if (onValueChangeListener != null) {
                    onValueChangeListener.onValueChange(view,
                            value);
                }
                inverseBindingListener.onChange();
            }
        };
    }

    view.setListener(newListener);
}

然后将addListener()替换为setListener(),您不需要removeListener(),因为您始终可以将监听器设置为null

您遇到的问题出现在您的组件中:

public String getValue() {
    return value;
}

您将返回setter最后设置的值,而不是EditText中的值。要解决这个问题:

public String getValue() {
    return txt.getText().toString();
}