Android双向数据绑定不适用于edittext和可观察变量

时间:2018-09-07 13:13:21

标签: android data-binding android-databinding

我有一个RecyclerView,其单元是通过数据绑定填充的。每个单元代表一个购物车的项目。每个单元格都包含一个EditText,它负责购物车中的产品数量。该数量在我的ViewModel中被重新定义为 observableInt 。当数量发生变化时,我想采取行动,并且为observableInt参数设置了 OnPropertyChangedCallback 侦听器。如果我使用 app:addTextChangedListener =“ @ {cartItemVM.quantityInputTextWatcher}” 来获取值并将其设置为可观察值,则其监听器将被调用多次(这也是因为我可以更改以及一些+-按钮的EditText值)。

我遇到了双向数据绑定,但是仍然无法使它起作用。这是我到目前为止的内容:

<QuantityEditText
                android:id="@+id/etQuantityInput"
                android:text="@{String.valueOf(cartItemVM.totalInCart)}"
                quantity="@={cartItemVM.totalInCart}"
                onQuantityChange="@{cartItemVM.onQuantityChange}"

这是我自定义的EditText:

public class QuantityEditText extends CustomEditText {

    private int quantity;
    private OnQuantityChangeListener onQuantityChangeListener;

    public interface OnQuantityChangeListener {
        void onQuantityChange(QuantityEditText view, int quantity);
    }

这是我的ViewModel类:

@InverseBindingMethods({
        @InverseBindingMethod(type = QuantityEditText.class, attribute = "quantity")
})
public class ProductInCartObservableViewModel{
    public final ObservableInt totalInCart;

@BindingAdapter(value = {"onQuantityChange", "quantityAttrChanged"},
            requireAll = false)
    public static void setQuantityAttrChanged(QuantityEditText view,
                                        final QuantityEditText.OnQuantityChangeListener listener,
                                        final InverseBindingListener quantityChange) {
        if (quantityChange == null) {
            view.setOnQuantityChangeListener(listener);
        } else {
            view.setOnQuantityChangeListener(new QuantityEditText.OnQuantityChangeListener() {
                @Override
                public void onQuantityChange(QuantityEditText view, int quantity) {
                    if (listener != null) {
                        listener.onQuantityChange(view, quantity);
                    }
                    view.setText(String.valueOf(quantity));
                    quantityChange.onChange();
                }
            });
        }
    }

    @BindingAdapter("quantity")
    public static void setQuantity(QuantityEditText view, int quantity) {
        if (quantity != view.getQuantity()) {
            view.setQuantity(quantity);
        }
    }

    @InverseBindingAdapter(attribute = "quantity")
    public static int getQuantity(QuantityEditText view) {
        int val = 0;
        if (!TextUtils.isEmpty(view.getText().toString())) {
            try {
                val = Integer.valueOf(view.getText().toString());
            } catch (IllegalArgumentException e) {
                Timber.e(e);
            }
        }

        // Won't let the user remove product from cart using the editText
        if (val <= 0) {
            val = 1;
        }

        if (val > 150) {
            val = 150;
        }

        return val;
    }

    public QuantityEditText.OnQuantityChangeListener onQuantityChange = new QuantityEditText.OnQuantityChangeListener() {
        @Override
        public void onQuantityChange(QuantityEditText view, int quantity) {
            if (quantity <= 0) {
                quantity = 0;
            }

            if (quantity > 150) {
                quantity = 150;
            }

            totalInCart.set(quantity);
        }
    };

实现是从不同的地方进行的,但我承认我还没有完全理解该过程,因此也将不胜感激。

1 个答案:

答案 0 :(得分:0)

我可以想到几种解决问题的方法。第一种是避免QuatityEditText,而直接设置数字。我更喜欢使用转换方法来定制双向绑定,因为它们往往更易于实现:

@InverseMethod("stringToInt")
public static String intToString(TextView view, int oldValue, int value) {
    NumberFormat numberFormat = getNumberFormat(view);
    try {
        String inView = view.getText().toString();
        int parsed = numberFormat.parse(inView).intValue();
        if (parsed == value) {
            return view.getText().toString();
        }
    } catch (ParseException e) {
        // old number was broken
    }
    return numberFormat.format(value);
}

public static int stringToInt(TextView view, int oldValue, String value) {
    NumberFormat numberFormat = getNumberFormat(view);
    try {
        return numberFormat.parse(value).intValue();
    } catch (ParseException e) {
        view.setError("Improper number format");
        return oldValue;
    }
}

private static NumberFormat getNumberFormat(View view) {
    Resources resources= view.getResources();
    Locale locale = resources.getConfiguration().locale;
    NumberFormat format =
            NumberFormat.getNumberInstance(locale);
    if (format instanceof DecimalFormat) {
        DecimalFormat decimalFormat = (DecimalFormat) format;
        decimalFormat.setGroupingUsed(false);
    }
    return format;
}

并且布局具有:

<EditText ...
            android:id="@+id/etQuantityInput"
            android:inputType="number"
            android:text="@={Converter.intToString(etQuantityInput, cartItemVM.totalInCart, cartItemVM.totalInCart)}"/>

如果您要一个新的quantityEditText,我认为最好是用一个属性来控制它,而不是同时使用字符串文本和int数量:

<QuantityEditText ...
            android:id="@+id/etQuantityInput"
            android:inputType="number"
            app:quantity="@={cartItemVM.totalInCart)}"/>

这是我整理的课,以使数量遵循文本:

public class QuantityEditText extends android.support.v7.widget.AppCompatEditText {
    public QuantityEditText(Context context) {
        super(context);
        initTextWatcher();
    }

    public QuantityEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        initTextWatcher();
    }

    public QuantityEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initTextWatcher();
    }

    private int quantity;
    private OnQuantityChangeListener onQuantityChangeListener;

    void initTextWatcher() {
        addTextChangedListener(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) {
                updateQuantityFromText();
            }
        });
    }

    public void setQuantity(int quantity) {
        if (updateQuantity(quantity)) {
            setText(getNumberFormat().format(quantity));
        }
    }

    public int getQuantity() {
        return quantity;
    }

    private boolean updateQuantity(int newQuantity) {
        if (this.quantity == newQuantity) {
            return false; // nothing to do
        }
        this.quantity = newQuantity;
        if (onQuantityChangeListener != null) {
            onQuantityChangeListener.onQuantityChange(this, quantity);
        }
        return true;
    }

    void updateQuantityFromText() {
        try {
            String inView = getText().toString();
            updateQuantity(getNumberFormat().parse(inView).intValue());
        } catch (ParseException e) {
            // Problem with the string format, so just don't update the quantity
        }
    }

    private NumberFormat getNumberFormat() {
        Resources resources = getResources();
        Locale locale = resources.getConfiguration().locale;
        NumberFormat format =
                NumberFormat.getNumberInstance(locale);
        if (format instanceof DecimalFormat) {
            DecimalFormat decimalFormat = (DecimalFormat) format;
            decimalFormat.setGroupingUsed(false);
        }
        return format;
    }

    public void setOnQuantityChangeListener(OnQuantityChangeListener listener) {
        onQuantityChangeListener = listener;
    }

    public interface OnQuantityChangeListener {
        void onQuantityChange(QuantityEditText view, int quantity);
    }
}

您还需要一种方法来设置InverseBindingListener并为数量设置双向数据绑定:

@InverseBindingMethods(
        @InverseBindingMethod(type = QuantityEditText.class, attribute = "quantity")
)
public class BindingAdapters {
    @BindingAdapter("quantityAttrChanged")
    public static void setQuantityAttrChanged(QuantityEditText view,
            final InverseBindingListener quantityChange) {
        QuantityEditText.OnQuantityChangeListener listener = null;
        if (quantityChange != null) {
            listener = new QuantityEditText.OnQuantityChangeListener() {
                @Override
                public void onQuantityChange(QuantityEditText view, int quantity) {
                    quantityChange.onChange();
                }
            };
        }
        view.setOnQuantityChangeListener(listener);
    }
}