更改LiveData后如何更新RecyclerView适配器?

时间:2020-07-03 02:15:33

标签: mvvm android-adapter android-livedata observers

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

如果我添加,删除或更新同一列表中的项目,则更改将发送到firebase,并且适配器将它们反映在屏幕上。可以。

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

但是,当查询发生时,适配器的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))

}
}

我的ViewModel:

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)

}

请注意。

1 个答案:

答案 0 :(得分:0)

好的,距离我问这个问题已有4天了,对项目感到有些沮丧后,我回到了StackOverFlow上发表自己的答案。

我显示的代码中有问题的问题是在适配器的addBalance方法中。

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

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

因此,我更改了方法的逻辑,以确保删除了我的对象,并仅在检查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 ...