如何将 LiveData<List<Entity>> 转换为 LiveData<List<Date>>

How to transform LiveData<List<Entity>> to LiveData<List<Date>>

我的项目是一个费用跟踪器,我在其中显示了一个日期列表,在该列表下我有一个在这些日期发生的费用列表。我嵌套了 RecyclerViews。 Parent RecyclerView 是所有费用的唯一日期列表。 Child RecyclerView 是费用清单(当然,在唯一日期下查看)。 我的 ViewModel 有一个 ExpenseEntity 的 LiveData 列表。 ViewModel 必须有一个包含唯一日期的 Date LiveData 列表。我从 Room 数据库中获取我的 ExpenseEntity 列表。

我的主要片段观察 ExpenseEntities 的 LiveData,因为那时我需要更新我的父子 recyclerviews。

我不知道如何使用 Transformations.map 来获得唯一日期的实时转换列表。我应该如何确保在更新 ExpenseEntity 的 LiveData 后始终更新 Dates 的 LiveData?

MainActivityViewModel.kt

class MainActivityViewModel(private val expenseDao: ExpenseDao) : ViewModel() {
    val allExpenses : LiveData<List<ExpenseEntity>> = expenseDao.fetchAllExpenses().asLiveData()
    val uniqueDates : LiveData<List<Date>> = Transformations.map(allExpenses) {
        it.map { expense ->
            expense.date!!
        }.distinct()
    }
...
}

ExpensesFragment.kt

val factory = MainActivityViewModelFactory((activity?.application as SimpleExpenseTrackerApp).db.expenseDao())
expensesViewModel = ViewModelProvider(this, factory).get(MainActivityViewModel::class.java)


binding.rvParentExpenseDates.layoutManager = LinearLayoutManager(requireContext())
expensesViewModel.allExpenses.observe(viewLifecycleOwner){ expensesList ->
    if (expensesList.isNotEmpty()){
       binding.rvParentExpenseDates.adapter = expensesViewModel.uniqueDates.value?.let {
           ParentDatesAdapter(it, expensesList) { expenseId ->
               Toast.makeText(requireContext(), "Clicked expense with id: $expenseId", Toast.LENGTH_LONG).show()
                }
            }
       binding.rvParentExpenseDates.visibility = View.VISIBLE
    } else {
       binding.rvParentExpenseDates.visibility = View.GONE
    }
}

ExpenseEntity.kt

@Entity(tableName = "expense-table")
data class ExpenseEntity(
    @PrimaryKey(autoGenerate = true)
    val id: Int = 0,
    @ColumnInfo(name = "date-time")
    val dateTime : Date?,
    val date : Date?,
    @ColumnInfo(name = "account-type")
    val accountType : String = "",
    val category : String = "",
    val amount : Double = 0.0,
    val currency : String = "",
    val note : String = ""

)

根据 documentation:

These methods permit functional composition and delegation of LiveData instances. The transformations are calculated lazily, and will run only when the returned LiveData is observed. Lifecycle behavior is propagated from the input source LiveData to the returned one.

这里的问题是您从未观察到转换后的 LiveData (uniqueDates) -- 您只检查了 value,因此从未应用转换。

如果您同时需要两者,一个选项是映射到联合视图中:

class MainActivityViewModel(private val expenseDao: ExpenseDao) : ViewModel() {
  val allExpenses : LiveData<List<ExpenseEntity>> =
    expenseDao.fetchAllExpenses().asLiveData()

  val allAndUniqueDatedExpenses: LiveData<Pair<List<ExpenseEntity>, List<Date>> = 
    Transformations.map(allExpenses) { expenses ->
      expenses to expenses.mapNotNull { it.date }.distinct()
    }
}

然后简单地观察这个连接值:

expensesViewModel.allAndUniqueDatedExpenses.observe(this) { (expenses, dates) ->
  binding.rvParentExpenseDates.adapter = 
    ParentDatesAdapter(dates, expenses) { expenseId ->
      Toast.makeText(...).show()
    }
}

但是,我要说的是,您实际上并不需要另一个 LiveData 转换。简单地进行内联转换:

expensesViewModel.allExpenses.observe(this) { expenses ->
  val dates = expenses.mapNotNull { it.date }.distinct()

  binding.rvParentExpenseDates.adapter =
    ParentDatesAdapter(dates, expenses) { expenseId ->
      Toast.makeText(...).show()
    }
}