【问题标题】:How I can update my adapter for RecyclerView after change my LiveData?更改 LiveData 后如何更新 RecyclerView 适配器?
【发布时间】:2020-10-23 17:06:10
【问题描述】:

我创建了一个片段,它在 onActivityCreated 方法中通过将查询限制为日历日期来获取 Firebase 数据。然后我在 ViewModel 中的 LiveData 上放置观察者,并将列表传递给我的适配器。

如果我在同一个列表中添加、删除或更新项目,更改会发送到 firebase,并且适配器会将它们反映在屏幕上。它工作正常。

但是,我正在尝试开发一个过滤器按钮,它基本上会更改 Firebase 查询的截止日期。当我选择一个特定的过滤器时,viewModel 需要从 Firebase 检索限制在过滤器日期的数据。这会生成一个新列表,其大小与之前的列表不同。

但是,当查询发生时,Adapter 的 getItemCount() 方法会存储最后一个列表的大小。这一事实使适配器感到困惑,并且函数 notifyItemInserted 和 notifyItemRemoved 最终会在更改过滤器后在屏幕上制作令人困惑的动画。不知道怎么回事。

如何正确观察 LiveData 并告诉适配器?我是在 MVVM 架构中犯了错误还是忘记了某些功能?

我的片段:

class HistoryFragment : Fragment(), OnItemMenuRecyclerViewClickListener {

private lateinit var mSecurityPreferences: SecurityPreferences
private lateinit var viewModel: BalancesViewModel
private lateinit var adapter: BalancesAdapter

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

    viewModel = ViewModelProvider(this).get(BalancesViewModel::class.java)
    adapter = BalancesAdapter(requireContext())
    mSecurityPreferences = SecurityPreferences(requireContext())

    return inflater.inflate(R.layout.fragment_history, container, false)
}

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)

    setupFilter()

    //Setup adapter
    adapter.listenerMenu = this
    recycler_view_history.adapter = adapter

    //Fetch data based in filter by date
    viewModel.fetchBalances(mSecurityPreferences.getStoredLong(FILTER_DATE))

    // Put logic to listen RealTimeUpdates
    viewModel.getRealTimeUpdates(mSecurityPreferences.getStoredLong(FILTER_DATE))

    viewModel.balances.observe(viewLifecycleOwner, Observer {
        adapter.setBalances(it)
    })

    viewModel.balance.observe(viewLifecycleOwner, Observer {
        adapter.addBalance(it)
    })

}

override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
    inflater.inflate(R.menu.history_menu_filter, menu)
    super.onCreateOptionsMenu(menu, inflater)
}

override fun onOptionsItemSelected(item: MenuItem): Boolean {
    when (item.itemId) {
        R.id.item_menu_filter_this_month -> {
            updateFilter(THIS_MONTH)
        }
        R.id.item_menu_filter_two_months -> {
            updateFilter(TWO_MONTHS)
        }
        R.id.item_menu_filter_last_six_months -> {
            updateFilter(LAST_SIX_MONTHS)
        }
        R.id.item_menu_filter_all -> {
            updateFilter(ALL_MONTHS)
        }
    }
    return super.onOptionsItemSelected(item)
}

private fun setupFilter() {
    var filterOption = mSecurityPreferences.getStoredLong(FILTER_DATE)
    if (filterOption == 0L){
        filterOption = HandleDate.getLongToFilter(LAST_SIX_MONTHS)
        mSecurityPreferences.storeLong(FILTER_DATE, filterOption)
    }
}

private fun updateFilter(filterOption: Int){
    val newFilterOption = HandleDate.getLongToFilter(filterOption)
    mSecurityPreferences.storeLong(FILTER_DATE, newFilterOption)
    updateUI()
}

private fun updateUI(){
    viewModel.fetchBalances(mSecurityPreferences.getStoredLong(FILTER_DATE))
    viewModel.getRealTimeUpdates(mSecurityPreferences.getStoredLong(FILTER_DATE))

}
}

我的视图模型:

class BalancesViewModel : ViewModel() {

private val userReference = FirebaseAuth.getInstance().currentUser!!.uid
private val dbUserReference = FirebaseDatabase.getInstance().getReference(userReference)

private val _balances = MutableLiveData<List<Balance>>()
val balances: LiveData<List<Balance>>
    get() = _balances

private val _balance = MutableLiveData<Balance>()
val balance: LiveData<Balance>
    get() = _balance

private val _result = MutableLiveData<Exception?>()
val result: LiveData<Exception?>
    get() = _result


fun addBalance(balance: Balance) {
    balance.id = dbUserReference.push().key
    dbUserReference.child(NODE_BALANCES).child(balance.id!!).setValue(balance)
        .addOnCompleteListener {
            if (it.isSuccessful) {
                _result.value = null
            } else {
                _result.value = it.exception
            }

        }
}

private val childEventListener = object : ChildEventListener {
    override fun onCancelled(error: DatabaseError) {
    }

    override fun onChildMoved(snapshot: DataSnapshot, p1: String?) {
    }

    override fun onChildChanged(snapshot: DataSnapshot, p1: String?) {
        val balance = snapshot.getValue(Balance::class.java)
        balance?.id = snapshot.key
        _balance.value = balance
    }

    override fun onChildAdded(snapshot: DataSnapshot, p1: String?) {
        val balance = snapshot.getValue(Balance::class.java)
        balance?.id = snapshot.key
        _balance.value = balance
    }

    override fun onChildRemoved(snapshot: DataSnapshot) {
        val balance = snapshot.getValue(Balance::class.java)
        balance?.id = snapshot.key
        balance?.isDeleted = true
        _balance.value = balance
    }
}

fun getRealTimeUpdates(longLimitDate: Long) {
    dbUserReference.child(NODE_BALANCES).orderByChild(COLUMN_DATE_MILLI)
        .startAt(longLimitDate.toDouble()).addChildEventListener(childEventListener)
}

fun fetchBalances(longLimitDate: Long) {
    dbUserReference.child(NODE_BALANCES).orderByChild(COLUMN_DATE_MILLI)
        .startAt(longLimitDate.toDouble())
        .addListenerForSingleValueEvent(object : ValueEventListener {
            override fun onCancelled(error: DatabaseError) {}

            override fun onDataChange(snapshot: DataSnapshot) {
                if (snapshot.exists()) {
                    val listBalances = mutableListOf<Balance>()
                    for (balanceSnapshot in (snapshot.children)) {
                        val balance = balanceSnapshot.getValue(Balance::class.java)
                        balance?.id = balanceSnapshot.key
                        balance?.let { listBalances.add(it) }
                    }
                    listBalances.sortByDescending { it.dateMilli }
                    _balances.value = listBalances
                   
                }
            }

        })
}

fun updateBalance(balance: Balance) {
    dbUserReference.child(NODE_BALANCES).child(balance.id!!).setValue(balance)
        .addOnCompleteListener {
            if (it.isSuccessful) {
                _result.value = null
            } else {
                _result.value = it.exception
            }

        }
}

fun deleteBalance(balance: Balance) {
    dbUserReference.child(NODE_BALANCES).child(balance.id!!).setValue(null)
        .addOnCompleteListener {
            if (it.isSuccessful) {
                _result.value = null
            } else {
                _result.value = it.exception
            }

        }
}

我的适配器:

class BalancesAdapter(private val context: Context) :
RecyclerView.Adapter<BalancesAdapter.BalanceViewModel>() {

private var balances = mutableListOf<Balance>()

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
    BalanceViewModel(
        LayoutInflater.from(parent.context)
            .inflate(R.layout.item_recyclerview_balance, parent, false)
    )
override fun getItemCount() = balances.size

override fun onBindViewHolder(holder: BalanceViewModel, position: Int) {
    holder.view.text_view_value_balance_item.text = balances[position].value
    holder.view.text_view_date_item.text = balances[position].date
}

fun setBalances(balances: List<Balance>) {
    this.balances = balances as MutableList<Balance>
    notifyDataSetChanged()
}

fun addBalance(balance: Balance) {
    val index = balances.indexOf(balance)
    if (!balances.contains(balance)) {
        balances.add(balance)
        notifyItemInserted(index)
    } else {
        if (balance.isDeleted) {
            balances.removeAt(index)
            notifyItemRemoved(index)
        } else {
            balances[index] = balance
        }
    }
    notifyItemRangeChanged(index, itemCount)
}

class BalanceViewModel(val view: View) : RecyclerView.ViewHolder(view)

}

请注意。

【问题讨论】:

    标签: mvvm android-adapter android-livedata observers


    【解决方案1】:

    好的,我问这个问题已经 4 天了,在对这个项目感到有点沮丧之后,我回到 StackOverFlow 上发布我自己的答案。

    我展示的代码中的问题在于我的适配器的 addBalance 方法。

    当我创建 Balance 数据模型时,我设置了 isDeleted 属性来标识它已被删除。进入 Firebase 后,它会收到一个 NULL 值,因此它不再存在。

    然后,由于我有两个侦听器(一个在 addListenerForSingleValueEvent 方法中定义,另一个在 addChildEventListener 方法中定义),当 Firebase 数据发生变化时,一个最终会触发另一个,但我不想详细讨论该问题。事实上,我检查了我的 addBalance 方法在我删除一个对象后被调用,导致该对象被插入回适配器的数据列表中,甚至在 Firebase 中的删除操作结束之前。

    所以我更改了我的方法的逻辑,以确保我的对象已被删除,并且仅在检查 isDeleted 属性后才将其包含在我的适配器列表中。

    fun dealWithBalance(balance: Balance){
    
        val index = balances.indexOf(balance)
        if(balance.isDeleted && balances.contains(balance)){
            balances.removeAt(index)
            notifyItemRemoved(index)
        } else if(!balance.isDeleted && !balances.contains(balance)){
            balances.add(balance)
        } else if(index >= 0){
            balances[index] = balance
            notifyItemChanged(index)
        }
    }
    

    我将 addBalance 重命名为 dealWithBalance...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-09-30
      • 1970-01-01
      • 1970-01-01
      • 2018-11-23
      • 2018-07-26
      • 2018-03-17
      • 1970-01-01
      相关资源
      最近更新 更多