簡體   English   中英

回收站視圖不會使用實時數據更新項目

[英]Recycler view doesn't update item with livedata

我有一個片段,其中使用為特定類別創建預算,如下所示: 在此處輸入圖像描述

它是這樣工作的:用戶在 NewBudgetFragment 中添加一個新的預算項目。 該項目顯示在 RecyclerView 的 BudgetFragment 中。 預算項目有 amountSpent 變量,每次用戶添加新交易時都應該更新(這發生在另一個片段中)。 但是在創建預算項目后,如果用戶在該特定類別上花錢,則 amountSpent 不會在 recyclerview 項目中更新。 我在 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)
        }
    }

假設您的 Budget Class 是一個數據類,那么內容比較應該可以進行,因此我們不應該對 Adapter 和 DiffUtil 使用的對象本身有任何問題。

但:

我看不到 ViewModel 代碼 - 您是否使用submitList向適配器提交相同的列表實例? 例如,您是否每次都在 ViewModel 中改變私有列表中的項目並在相同的實時數據上發布相同的列表?

如果是,那么這可能就是 RecyclerView 中的項目沒有被刷新的原因。 您需要使用舊列表的內容創建一個新的列表實例,然后將其發布到 LiveData 上。

在此處輸入圖像描述

示例 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
    }
}

我試圖在 Internet 上找到一些示例解決方案。 應該有,但是沒找到。 我發現其中很多都太短或太詳細。 所以我會把你需要的東西放在這里。 有一個涉及RecyclerView + LiveData的標准化模式,所以只要按照這個模式來使用RecyclerView + LiveData

分段:

override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
): View? {

    ...

    val recyclerView = view.findViewById(R.id.recyclerView)
    val myAdapter = MyAdapter()
    recyclerView.adapter = myAdapter
    // recyclerView.layoutManager = LinearLayoutManager(context) // You need this only if you haven't set it in xml

    db.task().getAll().observe(viewLifecycleOwner, Observer {
        myAdapter.submitList(it)
    })
}

這里做了一些重要的改變:

您必須在onCreateView().observe() ) 。 不僅針對這種特定情況,而且如果您正確使用LiveData ,通常您永遠不需要在任何其他地方調用.observe() viewLifecycleOwner是用於觀察LiveData的正確LifecycleOwner (除非你創建一個自定義的。)取決於用例,但通常每個RecyclerView實例化一個適配器,即使你的數據隨着時間的推移而變化。 您應該交換數據,而不是整個適配器。

我的適配器:

MyAdapter應該實現視圖將使用的.submitList()函數

class MyAdapter: RecyclerView.Adapter<ViewHolder>() {

    val myData = mutableListOf<Data>()

    fun submitList(newData: List<Data>) {
        myData.clear()
        myData.addAll(newData)
        notifyDataSetChanged()
    }
}

請注意, notifyDataSetChanged()實際上是通知適配器更新視圖的那個。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM