При отправке нового списка в RecyclerView ListAdapter проверка diff всегда возвращает true для areContentsTheSame () - PullRequest
1 голос
/ 27 июня 2019

Я использую архитектуру MVVM для создания простого приложения для заказа. Я использую RecyclerView в моем ProductsFragment, чтобы перечислить все продукты, которые можно заказать. Я также использую LiveData в моей ViewModel и наблюдаемую в моем фрагменте для проверки любых изменений в списке продуктов.

В моем элементе списка у меня есть 3 кнопки: 1 кнопка для добавления продукта в корзину, другая для увеличения количества, которое клиент хочет добавить в корзину, и третья для уменьшения количества, которое клиент хочет добавить в корзину. .

В моем классе данных о товаре, когда клиент нажимает кнопку увеличения или уменьшения, количество обновляется в модели данных.

Я также использую привязку данных для привязки продукта к макету элемента списка повторного просмотра и слушателям щелчков. Я использую ListAdapter для получения доступа к DiffUtil из коробки.

Проблема, с которой я сталкиваюсь, заключается в том, что, когда наблюдаемое получает уведомление, я хочу использовать метод submitList ListAdapter, чтобы в RecyclerView обновлялся только измененный элемент. Однако я заметил, что метод DiffUtil areContentsTheSame () всегда возвращает true. Таким образом, элемент списка не обновляется. Я не хочу использовать notifyDatasetChanged, так как это блокирует поток пользовательского интерфейса.

Другая проблема, с которой я сталкиваюсь, - это когда я добавляю товар в корзину, ссылка на товар сохраняется, поэтому, когда я добавляю товар в корзину, MutableLiveData также обновляется, когда я просто хочу, чтобы MutableLiveData был обновлено. Как я могу остановить ссылку на создание LiveData при добавлении товара в корзину?


ProductsViewModel

class ProductsViewModel : ViewModel() {

    // LIVE DATA
    private val _basket = MutableLiveData<Basket>()

    val basket: LiveData<Basket>
        get() = _basket

    private val _products = MutableLiveData<List<Product>>()

    val products: LiveData<List<Product>>
        get() = _products


    init {
        _basket.value = Basket()
        _products.value = dummyData
    }

    fun onIncrementProductQuantityButtonPressed(product: Product) {
        //product.quantity += 1
        //val newList = _products.value

        //_products.value = newList   
        val newProduct = product.copy(quantity = product.quantity.plus(1))
        _basket.value!!.updateProductInBasket(newProduct)
        _basket.value = _basket.value
    }

    fun onDecrementProductQuantityButtonPressed(product: Product) {
        if (product.quantity>1) {
            //product.quantity = product.quantity.minus(1)    
            val newProduct = product.copy(quantity = product.quantity.minus(1))
            _basket.value!!.updateProductInBasket(newProduct)
            _basket.value = _basket.value       
        }
    }
}

ProductsFragment

class ProductsFragment : Fragment() {
    private lateinit var viewModel: ProductsViewModel

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?): View? {
        // Inflate the layout for this fragment
        val binding: FragmentProductsBinding = DataBindingUtil.inflate(
            inflater, R.layout.fragment_products, container, false)

        viewModel = ViewModelProviders.of(this).get(ProductsViewModel::class.java)

        val adapter = ProductsAdapter(ProductListener { product, onClickType ->
            when(onClickType) {
                OnClickType.INCREMENT -> {
                    viewModel.onIncrementProductQuantityButtonPressed(product)
                }
                OnClickType.DECREMENT -> {
                    viewModel.onDecrementProductQuantityButtonPressed(product)
                }
                OnClickType.BASKET -> {
                    viewModel.addToBasketButtonPressed(product)
                }
            }
        })

        viewModel.products.observe(this, Observer { list ->
            adapter.submitList(list)
            //adapter.notifyDataSetChanged() // TODO: check why I have to do notifyDataSetChanged()
        })

        viewModel.basket.observe(this, Observer {
            activity?.invalidateOptionsMenu()
        })

        binding.viewModel = viewModel
        binding.lifecycleOwner = this
        binding.productsRecyclerView.adapter = adapter

        setHasOptionsMenu(true)

        return binding.root
    }

ProductsAdapter

class ProductsAdapter(private val clickListener: ProductListener) : ListAdapter<Product, ProductsAdapter.ProductViewHolder>(ProductDiffUtil()) {

    class ProductDiffUtil: DiffUtil.ItemCallback<Product>() {
        override fun areItemsTheSame(oldItem: Product, newItem: Product): Boolean {
            Log.d("Products", "Are items the same")
            return oldItem.name == newItem.name && oldItem.size == newItem.size
        }

        override fun areContentsTheSame(oldItem: Product, newItem: Product): Boolean {
            Log.d("Products", "Are contents the same ${oldItem == newItem}")
            Timber.d("Are contents the same ${oldItem == newItem}")
            Timber.d("OLD ITEM: $oldItem")
            Timber.d("NEW ITEM: $newItem")
            return oldItem == newItem //need to check this
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductViewHolder {
        return ProductViewHolder.from(parent)
    }

    override fun onBindViewHolder(holder: ProductViewHolder, position: Int) {
        val item = getItem(position)

        //use holder to access all the views in the card item
        holder.bind(clickListener, item)
    }

    class ProductViewHolder private constructor(private val binding: LayoutProductCardBinding) : RecyclerView.ViewHolder(binding.root) {

        fun bind(clickListener: ProductListener, item: Product) {
            binding.product = item
            binding.clickListener = clickListener
            binding.executePendingBindings()
        }

        companion object {
            fun from(parent: ViewGroup): ProductViewHolder {
                val layoutInflater = LayoutInflater.from(parent.context)
                val binding = LayoutProductCardBinding.inflate(layoutInflater, parent, false)

                return ProductViewHolder(binding)
            }
        }
    }

}

class ProductListener(val clickListener: (product: Product, clickType: OnClickType) -> Unit) {
    fun onAddToBasket(product: Product) = clickListener(product, OnClickType.BASKET)
    fun onDecrementProductQuantity(product: Product) = clickListener(product, OnClickType.DECREMENT)
    fun onIncrementProductQuantity(product: Product) = clickListener(product, OnClickType.INCREMENT)
}


enum class OnClickType { BASKET, DECREMENT, INCREMENT  }

1 Ответ

2 голосов
/ 27 июня 2019

Кажется, у вашей проблемы простое решение :). То, что вы делаете, это обновляет только значение продукта, который передается на виртуальную машину из поля зрения. следует сделать следующим образом: создать копию продукта, используя метод .copy (amount = ...), перезаписав количество. Затем замените свой предыдущий элемент в списке и передайте новый список в LiveData. Это также может быть причиной того, что вам нужно активно вызывать notifyDataSetChanged (согласно вашему комментарию в коде).

Надеюсь, это поможет. Ура! * * 1005

...