我创建了一个片段,该片段在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)
}
请注意。
答案 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 ...