0

我会很感激你的帮助。我想在送餐应用程序(uber eats,deliveroo)中建立一个自定义复选框组。当用户选择/取消选择一项时,它应该自动调整价格。此屏幕是使用每个项目的自定义复选框、自定义复选框组、双向数据绑定和环氧树脂构建的。选择/取消选择项目时,调用了正确的绑定适配器,但它永远不会更新视图模型。

在此处输入图像描述

这是自定义复选框。

   class CheckboxCustom @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : LinearLayout(context, attrs) {
    private val root: ViewGroup
    private val tvTitle: AppCompatTextView
    private val tvPrice: AppCompatTextView
    private val checkbox: AppCompatCheckBox

    init {
        inflate(context, R.layout.checkbox_custom, this)
        root = findViewById(R.id.root)
        tvTitle = findViewById(R.id.tvTitle)
        tvPrice = findViewById(R.id.tvPrice)
        checkbox = findViewById(R.id.checkbox)

        checkbox.isChecked

        root.setOnClickListener {
            checkbox.toggle()
            this.callOnClick()
        }
    }

    fun setTitle(title: String) {
        tvTitle.text = title
    }

    fun setPrice(price: String) {
        tvPrice.text = price
    }

    fun isChecked(): Boolean = checkbox.isChecked
}

这是自定义复选框组:

class CheckBoxGroup @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
        LinearLayout(context, attrs) {
    val parent: LinearLayout
    private lateinit var mOnCheckedChangeListener: OnCheckedChangeListener

    var totalPrice: Double = 0.0

    init {
        inflate(context, R.layout.checkbox_group, this)
        parent = findViewById(R.id.root)
    }


    fun setItems(toppings: Array<Topping>) {

        toppings.forEach { topping ->
            val checkBox = CheckboxCustom(context)
            checkBox.layoutParams = LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
            checkBox.setTitle(topping.name)
            checkBox.setPrice(topping.price.toString())
            checkBox.setOnClickListener {
                Timber.tag(TAG).d("checkbox checked: %s", checkBox.isChecked())
               if(checkBox.isChecked()) {
                   totalPrice+= topping.price
                   mOnCheckedChangeListener.onCheckedChange(totalPrice)
               } else {
                   totalPrice-= topping.price
                   mOnCheckedChangeListener.onCheckedChange(totalPrice)
               }
            }
            parent.addView(checkBox)
        }
    }

    fun setOnCheckedChangeListener(listener: OnCheckedChangeListener?) {
        listener?.let { this.mOnCheckedChangeListener = it}
    }

}

interface OnCheckedChangeListener {
    fun onCheckedChange(totalPrice: Double)

这是绑定适配器

@BindingAdapter("setItems")
    fun setItems(checkboxGroup: CheckBoxGroup, toppings: Array<Topping>) {
        checkboxGroup.setItems(toppings)
    }

    @BindingAdapter(value = ["setOnCheckedChangeListener", "totalPriceAttrChanged"] , requireAll = false)
    fun setOnCheckedChangeListener(checkboxGroup: CheckBoxGroup, listener: OnCheckedChangeListener,
                                   totalPriceAttrChanged: InverseBindingListener) {
        checkboxGroup.setOnCheckedChangeListener(listener)
        if (totalPriceAttrChanged != null) {
                    totalPriceAttrChanged.onChange()
        }
    }

    @BindingAdapter( "totalPrice")
    fun setTotalPrice(checkboxGroup: CheckBoxGroup, totalPrice: Double) {
        Timber.d("setTotalPrice binding called")
        if(checkboxGroup.totalPrice != totalPrice) {
            checkboxGroup.totalPrice.plus(1.0)
        }
    }

    @InverseBindingAdapter(attribute = "totalPrice")
    fun getTotalPrice(checkboxGroup: CheckBoxGroup): Double {
        Timber.d("getTotalPrice binding called")
        return checkboxGroup.totalPrice
    }

使用复选框的项目复选框,它是环氧树脂项目。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <variable
            name="toppings"
            type="com.jephtecolin.model.Topping[]" />
        <variable
            name="listener"
            type="com.jephtecolin.kwii.ui.custom.OnCheckedChangeListener" />
        <variable
            name="viewModel"
            type="com.jephtecolin.kwii.ui.consumable_detail.ConsumableDetailViewModel" />
    </data>

 <com.jephtecolin.kwii.ui.custom.CheckBoxGroup
     android:id="@+id/gp"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     app:totalPrice="@={viewModel.totalCost}"
     setOnCheckedChangeListener="@{listener}"
     setItems="@{toppings}"/>
</layout>

这是视图模型:

@HiltViewModel
class ConsumableDetailViewModel @Inject constructor() : ObservableViewModel() {


    val topping = Topping("1", "Hot Cheese", 30.00)

    val consumable = Consumable(
            "1",
            "Griot de boeuf",
            "Food",
            250.00,
            "https://www.innovatorsmag.com/wp-content/uploads/2017/03/IF-Burger-1024x772.jpg",
            "Griot de boeuf en sauce, servis avec des bananes pese, des frites et du salade",
            "Boeuf, Banane, Choux, Radis",
    )

    val totalCost: ObservableDouble = ObservableDouble(0.0)
        @Bindable get() {
            return field
        }

}

这是自定义视图模型

open class ObservableViewModel : ViewModel(), Observable {

    private val callbacks: PropertyChangeRegistry by lazy { PropertyChangeRegistry() }

    override fun addOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback) {
        callbacks.add(callback)
    }

    override fun removeOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback) {
        callbacks.remove(callback)
    }

    /**
     * Notifies listeners that all properties of this instance have changed.
     */
    @Suppress("unused")
    fun notifyChange() {
            callbacks.notifyCallbacks(this, 0, null)
    }

    /**
     * Notifies listeners that a specific property has changed. The getter for the property
     * that changes should be marked with [Bindable] to generate a field in
     * `BR` to be used as `fieldId`.
     *
     * @param fieldId The generated BR id for the Bindable field.
     */
    fun notifyPropertyChanged(fieldId: Int) {
        callbacks.notifyCallbacks(this, fieldId, null)
    }
}

此代码来自片段,并且没有被触发:

viewModel.totalCost.addOnPropertyChangedCallback(
                object : Observable.OnPropertyChangedCallback() {
            override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
                Toast.makeText(context, (sender as ObservableDouble).get().toString(), Toast.LENGTH_LONG).show()
                Timber.d(" totalPrice :" + (sender as ObservableDouble).get())
            }
        })

这段代码也来自片段,也没有被触发

  rvConsumableDetail.withModels {
     itemCheckbox {
                id("topping")
                toppings(toppings.toTypedArray())
                listener(object : OnCheckedChangeListener {
                    override fun onCheckedChange(totalPrice: Double) {
                        // todo("Not yet implemented")
                        Timber.d("itemCheckBox totalPrice :" + totalPrice)
                        Toast.makeText(context, "itemCheckBox totalPrice :" + totalPrice, Toast.LENGTH_LONG).show()
                    }
                })
            }
}

绑定适配器似乎工作正常,当我在其中放置断点时它可以工作,但由于某种原因,totalPrice从片段中观察变化永远不会工作。

感谢您的帮助

4

1 回答 1

0

您在绑定适配器方法中有 2 个用于检查更改侦听器的语句。这将导致侦听器对象被替换。所以你没有得到事件。

        checkboxGroup.setOnCheckedChangeListener(listener)
        checkboxGroup.setOnCheckedChangeListener(object : OnCheckedChangeListener {
            override fun onCheckedChange(totalPrice: Double) {
                Timber.d("binding adapter works")
                setTotalPrice(checkboxGroup, totalPrice)
                if (totalPriceAttrChanged != null) {
                    totalPriceAttrChanged.onChange()
                }
            }
        })
于 2021-07-29T06:40:29.397 回答