[英]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.