Как получить элемент RecyclerView для обновления себя из значения LiveData в своем объекте привязки с помощью BindingAdapter? - PullRequest
0 голосов
/ 23 октября 2019

У меня есть 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>

My 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, консоль каждый раз правильно печатает значения до и после, поэтому я знаю, что значение liked LiveData фактически изменяется, но представление не обновляется соответствующим образом, потому что адаптер привязкивызывается только один раз (в binding.executePendingBindings(), я думаю, когда RecyclerView связывает ViewHolder, а не каждый раз, когда меняются LiveData).

Я хочу, чтобы код BindingAdapter запускался каждый разliked Изменения LiveData.

Когда я делаю это с макетом ViewModel и Fragment, каждый раз, когда LiveData изменяется в ViewModel, изменения Viewпотому что XML ссылается на LiveData, но я не знаю, почему, когда речь заходит о ProductCard и макете элемента макета просмотра, даже если 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?

1 Ответ

0 голосов
/ 25 октября 2019

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

см. Документацию .

...