Android 双向数据绑定不适用于编辑文本和可观察变量



>我有一个RecyclerView,其单元格通过数据绑定填充。每个单元格表示产品购物车的一个项目。每个单元格都包含一个 EditText,负责购物车中的产品数量。在我的视图模型中,该数量被重新定义为可观察的Int。当数量发生变化时,我想执行操作,并且为可观察性Int参数设置了一个OnPropertyChanged回调侦听器。如果我使用app:addTextChangedListener="@{cartItemVM.quantityInputTextWatcher}">来获取值并将其设置为可观察量,它的侦听器将被调用多次(这也是因为我也可以从一些 + - 按钮更改 EditText 的值(。

我遇到了双向数据绑定,但仍然无法使其工作。这是我到目前为止所拥有的:

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

这是我的自定义编辑文本:

public class QuantityEditText extends CustomEditText {
private int quantity;
private OnQuantityChangeListener onQuantityChangeListener;
public interface OnQuantityChangeListener {
void onQuantityChange(QuantityEditText view, int quantity);
}

这是我的视图模型类:

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

实现来自不同的地方,但我承认我还没有完全理解这个过程,所以解释也将不胜感激。

我可以想到几种解决问题的方法。首先是避免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,我认为最好有一个属性控制它,而不是同时控制字符串文本和整数:

<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 并为 quantity 设置双向数据绑定:

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

最新更新