如何使用BindingAdapter从其绑定对象中的LiveData值获取RecyclerView项进行自身更新?

问题描述 投票:0回答:1

我有一个recyclerview.widget.ListAdapter可以在网格中显示一些卡片。当我单击卡时,它会出现在网格的前面,翻转,并且当您看到前面时,您应该会喜欢它。我在卡片正面的一角有一个ImageView,应该在喜欢与否之间切换,显示我已经定义的另一个可绘制对象。

我的ViewHolder看起来像这样:

class GameCardViewHolder(private val binding : ItemGameCardBinding) :
        RecyclerView.ViewHolder(binding.root){

    companion object {
        fun from(parent: ViewGroup): GameCardViewHolder {
            val layoutInflater = LayoutInflater.from(parent.context)
            val binding = ItemGameCardBinding.inflate(layoutInflater, parent, false)
            return GameCardViewHolder(binding)
        }
        ...
    }

    fun bind(productCard: ProductCard, listener: OnClickListener){
        binding.productCard = productCard
        binding.executePendingBindings()
        ...  
    }
}

我的item_game_card.xml看起来像这样:

<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="productCard"
            type="company.app.model.ProductCard" />
    </data>
    <androidx.constraintlayout.widget.ConstraintLayout>
    ...
        <ImageView
            android:id="@+id/gameItemLikeImageView"
            android:layout_width="0dp"
            android:layout_height="0dp"
            ...
            app:imageDrawable="@{productCard.liked}"
            android:onClick="@{() -> productCard.toggleLike()}"
        />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

我的BindingAdapters.kt:

@BindingAdapter("imageDrawable")
fun bindImageDrawable(imgView: ImageView, boolean: LiveData<Boolean>?) {
    Timber.d("Binding liked: $boolean")
    when(boolean?.value){
        true -> imgView.setImageResource(R.drawable.ic_card_like)
        false ->  imgView.setImageResource(R.drawable.ic_card_dislike)
    }
}

和我的ProductCard.kt:

data class ProductCard(
    val position: Int,
    val product: Product
){
    ...
    private val _liked = MutableLiveData(false)
    val liked : LiveData<Boolean>
        get() = _liked

    fun toggleLikeProduct(){
        Timber.d("Toggle like before: ${_liked.value}")
        val oldValue = _liked.value!!
        _liked.value = !oldValue
        Timber.d("Toggle like after: ${_liked.value}")
    }
}

[当我单击ImageView时,控制台每次都会正确打印出before和after的值,因此我知道liked LiveData的值实际上正在更改,但是View没有相应地更新,这是因为绑定适配器仅被调用一次(在binding.executePendingBindings()中,我想是当RecyclerView绑定ViewHolder时,而不是每次LiveData都更改时)。

[我希望每次liked LiveData更改时都运行BindingAdapter代码。

[当我使用ViewModel和Fragment布局执行此操作时,每当LiveData在ViewModel中更改时,视图都会更改,因为xml引用了该LiveData,但是我不知道为什么,当涉及到ProductCard和recyclerview项目布局时,即使xml引用了LiveData,视图也不会更改,但xml实际上并未绑定到LiveData,这就是拥有LiveData的全部意义。

我已经以一种非常粗鲁,毫不含糊的方式实现了这种行为,在该视图上设置一个onClickListener,并强迫它在OnBindViewHolder上像这样改变它的资源可绘制性:

fun bind(productCard: ProductCard, listener: OnClickListener){
    binding.productCard = productCard
    binding.executePendingBindings()
    binding.gameItemLikeImageView.setOnClickListener {
        it?.let {
            val card = binding.productCard
            Timber.d("Tried changing liked status: ${card!!.liked}")
            val old = card.liked
            card.liked = !old
            binding.productCard = card
            val card2 = binding.productCard
            Timber.d("Tried changing liked status: ${card2!!.liked}")
            if (!old){
                it.setBackgroundResource(R.drawable.ic_card_like)
            }
            else {
                it.setBackgroundResource(R.drawable.ic_card_dislike)
            }
        }
    }
    ...
}

但是我真的很希望每张卡的行为都受绑定到的ProductCard对象的状态控制。

[我也尝试过每次单击都强制拨打binding.executePendingBindings(),但也没有用。

我如何制作item_game_card.xml来观察LiveData中的更改?

kotlin android-databinding android-livedata android-viewholder android-binding-adapter
1个回答
0
投票

app:imageDrawable将不接受任何LiveData<Boolean>,我想知道为什么它不是LiveData<Product>?尝试像这样绑定初始值:

app:imageDrawable="@{product.liked ? R.drawable_liked : R.drawable_neutral}"

对于LiveData,需要为绑定设置LifecycleOwner

class ProductsActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {

        // Inflate view and obtain an instance of the binding class.
        val binding: ProductsBinding = DataBindingUtil.setContentView(this, R.layout.products)

        // Specify the current activity as the lifecycle owner.
        binding.setLifecycleOwner(this)
    }
}

请参见documentation

© www.soinside.com 2019 - 2024. All rights reserved.