我会很感激你的帮助。我想在送餐应用程序(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
从片段中观察变化永远不会工作。
感谢您的帮助