Recycler View不通过LiveData更新项。

3

我有一个片段,用户可以在其中为特定类别创建预算,如下所示: enter image description here

它的工作原理是这样的:用户在NewBudgetFragment中添加新的预算项。该项将在recyclerview中显示在BudgetFragment中。预算项具有amountSpent变量,每当用户添加新的交易时(这发生在另一个片段中),该变量应更新。但是,在创建预算项之后,如果用户在该特定类别上花费了钱,则recyclerview项中的amountSpent不会得到更新。我已经在BudgetAdapter中使用了LiveData和DiffUtil,但我无法弄清楚为什么它没有得到更新。

这是BudgetAdapter:

class BudgetAdapter() : ListAdapter<Budget, BudgetAdapter.BudgetViewHolder>(DiffCallback()) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BudgetViewHolder {
        val binding =
            BudgetItemLayoutBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return BudgetViewHolder(binding)
    }

    override fun onBindViewHolder(holder: BudgetViewHolder, position: Int) {
        val currentItem = getItem(position)
        holder.bind(currentItem, position)
    }

    class BudgetViewHolder(val binding: BudgetItemLayoutBinding) :
        RecyclerView.ViewHolder(binding.root) {
        fun bind(budget: Budget, position: Int) {
            binding.apply {
                tvBudgetName.text = budget.name
                tvBudgetLimit.text = budget.limit.toString()
                tvAmountSpent.text = budget.amountSpent.toString()
                tvPercentageSpent.text = ((budget.amountSpent/budget.limit)*100).toInt().toString() + "%"
            }
        }
    }

    class DiffCallback : DiffUtil.ItemCallback<Budget>() {
        override fun areItemsTheSame(oldItem: Budget, newItem: Budget): Boolean {
            return oldItem.id == newItem.id
        }

        override fun areContentsTheSame(oldItem: Budget, newItem: Budget): Boolean {
            return oldItem == newItem
        }

    }
}

以下是创建新预算项的方法: NewBudgetFragment:

...
viewModel.transactions.observe(viewLifecycleOwner) { it ->
                            transactionList = it.filter { it.category == listCategory[selectedCategoryIndex].name }
                            amountSpent = transactionList.sumOf { it.amount }
                        }
...

if (budgetName.isNotEmpty() && budgetLimit.isNotEmpty() && budgetCategory != null) {
                            viewModel.addBudget(
                                name = budgetName,
                                limit = budgetLimit.toDouble(),
                                amountSpent=amountSpent,
                                category = budgetCategory.name)

这是BudgetFragment.kt文件,适配器在其中:

class BudgetFragment : Fragment(R.layout.fragment_budget),BudgetAdapter.OnItemClickListener {
    private lateinit var binding: FragmentBudgetBinding
    private val viewModel: SharedViewModel by activityViewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding = FragmentBudgetBinding.bind(view)
        val budgetAdapter = BudgetAdapter(this)

        val toolbar = binding.toolbar.root
        toolbar.title = "Budget"
        (requireActivity() as MainActivity).setSupportActionBar(toolbar)

        binding.apply {
            rvBudget.apply {
                adapter = budgetAdapter
                setHasFixedSize(true)
            }
        }
        viewModel.budgets.observe(viewLifecycleOwner){
            if(it.isNotEmpty()){
                binding.rvBudget.visibility = View.VISIBLE
                binding.tvNoBudget.visibility = View.INVISIBLE
            }else{
                binding.rvBudget.visibility = View.INVISIBLE
                binding.tvNoBudget.visibility = View.VISIBLE
            }
            budgetAdapter.submitList(it)

        }

        binding.btAddBudget.setOnClickListener {
            val action = BudgetFragmentDirections.actionBudgetFragmentToNewBudgetFragment()
            findNavController().navigate(action)
        }
    }


2
你必须展示你的全部代码。像这样无法弄清楚哪里写了什么。 - Samir Alakbarov
budgetAdapter.submitList方法实现在哪里?为什么不把你的代码作为一个整体呢?以这种方式我们无法帮助你。 - Samir Alakbarov
1
你如何在你的 ViewModel 中实现 addBudget 方法?确保每次添加预算时创建一个新的预算列表。当你向已经提交给 budgetAdapter 的当前预算中添加新的预算时,DiffUtil 将把它视为相同的列表,这就是为什么你的列表不会更新的原因。这是因为在 Java/Kotlin 中,当你把一个 List 作为参数传递时,它是按引用传递的。 - Sovathna Hong
1
实际上,在使用submitList函数时,根据我在文档中看到的,没有必要调用notifyDataSetChanged。@Ranjithkumar - Samir Alakbarov
预算模型是 Kotlin 数据类。这可能是由于您的 diff 工具,请调试一下。检查您分配的 IDs。 - Deepak
显示剩余19条评论
1个回答

0
假设您的Budget类是数据类,那么内容比较应该可以工作,因此我们不应该在适配器和DiffUtil中使用的对象本身上出现任何问题。
但是:
我看不到ViewModel代码 - 您是否使用submitList将相同的List实例提交给Adapter?例如,您是否在ViewModel中的私有List中变异项目并每次发布相同的List?如果是,则这可能是RecyclerView中的项目未被刷新的原因。您需要创建旧List内容的新List实例,然后将其发布到LiveData上。

enter image description here

例如ViewModel,如果您不想覆盖“submitList”的行为,在其中清除先前的数据并添加新数据,然后自己调用notifiyDatasetChanged()

class MyBudgetViewModel : ViewModel() {
    // Important that its a data class, to actually have a content sensistive equals comparison

    // if you don't have an ID, you have to work with list indexes when finding
    // and updating this item
    data class Budget(val id: Int, val amount: Double)

    private val _budgets = MutableLiveData<List<Budget>>()
    val budgets: LiveData<List<Budget>>
        get() = _budgets


    init {
        _budgets.value = listOf(Budget(1, 20.0))
    }

    fun onBudgetChanged(id: Int, newBudget: Double) {
        // depends on your setup and how you fill the initial list, this maybe should never be null
        // by the time you call onBudgetChanged or something similar
        val oldList = _budgets.value ?: emptyList()

        // unused example variable - if you want to copy the list 1 by 1, ArrayList takes another list as Constructor 
        val newListUnchanged = ArrayList(oldList)

        // map returns a new instance of the list.
        val newList = oldList.map { oldItem ->
            if (oldItem.id == id) {
                oldItem.copy(amount = newBudget)
            } else oldItem
        }
        _budgets.value = newList
    }
}

我在ViewModel中没有对列表进行任何操作。如何创建一个包含旧内容的新列表? - jane
我在答案中为您编写了一些代码,希望能对您有所帮助。否则,您可以在适配器中覆盖submitList方法,以操作适配器的旧后备列表,然后自行调用notifyDatasetChanged()方法。 - Radu M

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接