无法使用观察室实体的结果
Unable to use results from Observing Room entity
我的目标
我正在尝试创建一个包含 Routine
实体列表的 RecyclerView。我在 Routine
实体和 Exercise
实体之间创建了多对多关系。我的 RecyclerView 需要包含 Routine
的名称以及与 Routine
.
关联的 Exercise
名称的字符串
运动
@Entity
data class Exercise(
@PrimaryKey(autoGenerate = true)
val exerciseId: Int,
val exerciseName: String
)
日常
@Entity
data class Routine(
@PrimaryKey(autoGenerate = true)
val routineId: Int,
val routineName: String
)
ExerciseRoutineJoin
@Entity(
primaryKeys = arrayOf("exerciseId", "routineId"),
foreignKeys = arrayOf(
ForeignKey(
entity = Exercise::class,
parentColumns = arrayOf("exerciseId"),
childColumns = arrayOf("exerciseId"),
onDelete = ForeignKey.NO_ACTION
),
ForeignKey(
entity = Routine::class,
parentColumns = arrayOf("routineId"),
childColumns = arrayOf("routineId"),
onDelete = ForeignKey.NO_ACTION
)
)
)
data class ExerciseRoutineJoin(val exerciseId: Int, val routineId: Int)
AppDatabase
@Database(entities = arrayOf(Routine::class, Exercise::class, ExerciseRoutineJoin::class), version = 1, exportSchema = false)
public abstract class AppDatabase : RoomDatabase() {
abstract fun routineDao(): RoutineDao
abstract fun exerciseDao(): ExerciseDao
abstract fun exerciseRoutineJoinDao(): ExerciseRoutineJoinDao
companion object {
// Singleton prevents multiple instances of database opening at the
// same time.
@Volatile
private var INSTANCE: WordRoomDatabase? = null
fun getDatabase(context: Context): WordRoomDatabase {
val tempInstance = INSTANCE
if (tempInstance != null) {
return tempInstance
}
synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"app_database"
).build()
INSTANCE = instance
return instance
}
}
}
}
在包含此 RecyclerView 的片段中,我正在观察 RoutineViewModel
并将例程发送到 RecyclerView 适配器。
private fun sendRoutinesToAdapter(adapter: RoutineAdapter) {
routineViewModel.allRoutines.observe(viewLifecycleOwner, Observer { routines ->
routines?.let { adapter.setRoutines(it) }
})
}
我的问题
因为 ExerciseRoutineJoin
在其 table 中没有 Exercise
名称,我正在创建一个 Mutable 的 ExerciseRoutineJoins 地图,其中例程 ID 作为键和与该 ID 关联的所有练习名称的字符串作为值。
private fun combineExerciseNamesForRoutine(
routineId: Int,
joins: List<ExerciseRoutineJoin>
): String {
exerciseViewModel = ViewModelProvider(this).get(ExerciseViewModel::class.java)
val builder = StringBuilder()
joins.forEach {
if (it.routineId == routineId) {
// TODO Why is builder not appending the string?
exerciseViewModel.getExerciseById(it.exerciseId)?.observe(viewLifecycleOwner,
Observer { exercise -> builder.append(exercise.exerciseName) })
}
}
return builder.toString()
}
我的观察者似乎没有为我的 StringBuilder 提供练习名称。我试过使用 ExerciseViewModel
return List
而不是 LiveData<List<Exercise>>
但这会导致从主线程调用数据库时出现大量问题。
如何使用观察功法的结果?有没有更聪明的方法来实现 RecyclerView 的这个目标与来自不同实体的内容?
StartWorkoutFragment.kt
class StartWorkoutFragment : Fragment() {
private lateinit var routineViewModel: RoutineViewModel
private lateinit var exerciseViewModel: ExerciseViewModel
private lateinit var exerciseRoutineJoinViewModel: ExerciseRoutineJoinViewModel
private var adapter: RoutineAdapter? = null
companion object {
fun newInstance(): StartWorkoutFragment {
return StartWorkoutFragment()
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_start_workout, container, false)
val activity = activity as Context
val recyclerView: RecyclerView = view.findViewById(R.id.rv_routines)
val adapter = RoutineAdapter(activity)
recyclerView.layoutManager = GridLayoutManager(activity, 2)
recyclerView.adapter = adapter
routineViewModel = ViewModelProvider(this).get(RoutineViewModel::class.java)
setUpRecyclerViewContent(adapter)
setUpAddRoutineButton(view)
return view
}
private fun setUpRecyclerViewContent(adapter: RoutineAdapter) {
sendRoutinesToAdapter(adapter)
getExerciseRoutineJoinsFromDatabase()
}
private fun sendRoutinesToAdapter(adapter: RoutineAdapter) {
routineViewModel.allRoutines.observe(viewLifecycleOwner, Observer { routines ->
routines?.let { adapter.setRoutines(it) }
})
}
private fun getExerciseRoutineJoinsFromDatabase() {
val routineIdWithExercisesAsStringPairs = mutableMapOf<Int, String>() // create empty map
exerciseRoutineJoinViewModel =
ViewModelProvider(this).get(ExerciseRoutineJoinViewModel::class.java)
exerciseRoutineJoinViewModel.allExerciseRoutineJoins.observe(
viewLifecycleOwner,
Observer { joins ->
joins?.let { it ->
it.forEach {
if (!routineIdWithExercisesAsStringPairs.containsKey(it.routineId)) // if the routine ID hasn't already been mapped, map the routine ID to the exercise String
routineIdWithExercisesAsStringPairs[it.routineId] =
combineExerciseNamesForRoutine(it.routineId, joins)
}
}
})
adapter?.setRoutineIdWithExercisesAsStringPairs(routineIdWithExercisesAsStringPairs) // send the map to the RecyclerView adapter
}
private fun combineExerciseNamesForRoutine(
routineId: Int,
joins: List<ExerciseRoutineJoin>
): String {
exerciseViewModel = ViewModelProvider(this).get(ExerciseViewModel::class.java)
val builder = StringBuilder()
joins.forEach {
if (it.routineId == routineId) {
// TODO Why is builder not appending the string?
exerciseViewModel.getExerciseById(it.exerciseId)?.observe(viewLifecycleOwner,
Observer { exercise -> builder.append(exercise.exerciseName) })
}
}
return builder.toString()
}
private fun setUpAddRoutineButton(view: View) {
val button: Button = view.findViewById(R.id.buttonAddRoutine)
button.setOnClickListener {
view.findNavController().navigate(R.id.navigation_add_routine)
}
}
}
您可以按照说明从数据库中查询包含练习的例程here:
data class RoutineWithExercises(
@Embedded val routine: Routine,
@Relation(
parentColumn = "routineId",
entityColumn = "exerciseId",
associateBy = @Junction(ExerciseRoutineJoin::class)
)
val exercices: List<Exercise>
)
然后在你的 RoutineDao 中:
@Transaction
@Query("SELECT * FROM Routine")
fun getRoutinesWithExercises(): List<RoutineWithExercises>
然后你就有了例程和练习。
我的目标
我正在尝试创建一个包含 Routine
实体列表的 RecyclerView。我在 Routine
实体和 Exercise
实体之间创建了多对多关系。我的 RecyclerView 需要包含 Routine
的名称以及与 Routine
.
Exercise
名称的字符串
运动
@Entity
data class Exercise(
@PrimaryKey(autoGenerate = true)
val exerciseId: Int,
val exerciseName: String
)
日常
@Entity
data class Routine(
@PrimaryKey(autoGenerate = true)
val routineId: Int,
val routineName: String
)
ExerciseRoutineJoin
@Entity(
primaryKeys = arrayOf("exerciseId", "routineId"),
foreignKeys = arrayOf(
ForeignKey(
entity = Exercise::class,
parentColumns = arrayOf("exerciseId"),
childColumns = arrayOf("exerciseId"),
onDelete = ForeignKey.NO_ACTION
),
ForeignKey(
entity = Routine::class,
parentColumns = arrayOf("routineId"),
childColumns = arrayOf("routineId"),
onDelete = ForeignKey.NO_ACTION
)
)
)
data class ExerciseRoutineJoin(val exerciseId: Int, val routineId: Int)
AppDatabase
@Database(entities = arrayOf(Routine::class, Exercise::class, ExerciseRoutineJoin::class), version = 1, exportSchema = false)
public abstract class AppDatabase : RoomDatabase() {
abstract fun routineDao(): RoutineDao
abstract fun exerciseDao(): ExerciseDao
abstract fun exerciseRoutineJoinDao(): ExerciseRoutineJoinDao
companion object {
// Singleton prevents multiple instances of database opening at the
// same time.
@Volatile
private var INSTANCE: WordRoomDatabase? = null
fun getDatabase(context: Context): WordRoomDatabase {
val tempInstance = INSTANCE
if (tempInstance != null) {
return tempInstance
}
synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"app_database"
).build()
INSTANCE = instance
return instance
}
}
}
}
在包含此 RecyclerView 的片段中,我正在观察 RoutineViewModel
并将例程发送到 RecyclerView 适配器。
private fun sendRoutinesToAdapter(adapter: RoutineAdapter) {
routineViewModel.allRoutines.observe(viewLifecycleOwner, Observer { routines ->
routines?.let { adapter.setRoutines(it) }
})
}
我的问题
因为 ExerciseRoutineJoin
在其 table 中没有 Exercise
名称,我正在创建一个 Mutable 的 ExerciseRoutineJoins 地图,其中例程 ID 作为键和与该 ID 关联的所有练习名称的字符串作为值。
private fun combineExerciseNamesForRoutine(
routineId: Int,
joins: List<ExerciseRoutineJoin>
): String {
exerciseViewModel = ViewModelProvider(this).get(ExerciseViewModel::class.java)
val builder = StringBuilder()
joins.forEach {
if (it.routineId == routineId) {
// TODO Why is builder not appending the string?
exerciseViewModel.getExerciseById(it.exerciseId)?.observe(viewLifecycleOwner,
Observer { exercise -> builder.append(exercise.exerciseName) })
}
}
return builder.toString()
}
我的观察者似乎没有为我的 StringBuilder 提供练习名称。我试过使用 ExerciseViewModel
return List
而不是 LiveData<List<Exercise>>
但这会导致从主线程调用数据库时出现大量问题。
如何使用观察功法的结果?有没有更聪明的方法来实现 RecyclerView 的这个目标与来自不同实体的内容?
StartWorkoutFragment.kt
class StartWorkoutFragment : Fragment() {
private lateinit var routineViewModel: RoutineViewModel
private lateinit var exerciseViewModel: ExerciseViewModel
private lateinit var exerciseRoutineJoinViewModel: ExerciseRoutineJoinViewModel
private var adapter: RoutineAdapter? = null
companion object {
fun newInstance(): StartWorkoutFragment {
return StartWorkoutFragment()
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_start_workout, container, false)
val activity = activity as Context
val recyclerView: RecyclerView = view.findViewById(R.id.rv_routines)
val adapter = RoutineAdapter(activity)
recyclerView.layoutManager = GridLayoutManager(activity, 2)
recyclerView.adapter = adapter
routineViewModel = ViewModelProvider(this).get(RoutineViewModel::class.java)
setUpRecyclerViewContent(adapter)
setUpAddRoutineButton(view)
return view
}
private fun setUpRecyclerViewContent(adapter: RoutineAdapter) {
sendRoutinesToAdapter(adapter)
getExerciseRoutineJoinsFromDatabase()
}
private fun sendRoutinesToAdapter(adapter: RoutineAdapter) {
routineViewModel.allRoutines.observe(viewLifecycleOwner, Observer { routines ->
routines?.let { adapter.setRoutines(it) }
})
}
private fun getExerciseRoutineJoinsFromDatabase() {
val routineIdWithExercisesAsStringPairs = mutableMapOf<Int, String>() // create empty map
exerciseRoutineJoinViewModel =
ViewModelProvider(this).get(ExerciseRoutineJoinViewModel::class.java)
exerciseRoutineJoinViewModel.allExerciseRoutineJoins.observe(
viewLifecycleOwner,
Observer { joins ->
joins?.let { it ->
it.forEach {
if (!routineIdWithExercisesAsStringPairs.containsKey(it.routineId)) // if the routine ID hasn't already been mapped, map the routine ID to the exercise String
routineIdWithExercisesAsStringPairs[it.routineId] =
combineExerciseNamesForRoutine(it.routineId, joins)
}
}
})
adapter?.setRoutineIdWithExercisesAsStringPairs(routineIdWithExercisesAsStringPairs) // send the map to the RecyclerView adapter
}
private fun combineExerciseNamesForRoutine(
routineId: Int,
joins: List<ExerciseRoutineJoin>
): String {
exerciseViewModel = ViewModelProvider(this).get(ExerciseViewModel::class.java)
val builder = StringBuilder()
joins.forEach {
if (it.routineId == routineId) {
// TODO Why is builder not appending the string?
exerciseViewModel.getExerciseById(it.exerciseId)?.observe(viewLifecycleOwner,
Observer { exercise -> builder.append(exercise.exerciseName) })
}
}
return builder.toString()
}
private fun setUpAddRoutineButton(view: View) {
val button: Button = view.findViewById(R.id.buttonAddRoutine)
button.setOnClickListener {
view.findNavController().navigate(R.id.navigation_add_routine)
}
}
}
您可以按照说明从数据库中查询包含练习的例程here:
data class RoutineWithExercises(
@Embedded val routine: Routine,
@Relation(
parentColumn = "routineId",
entityColumn = "exerciseId",
associateBy = @Junction(ExerciseRoutineJoin::class)
)
val exercices: List<Exercise>
)
然后在你的 RoutineDao 中:
@Transaction
@Query("SELECT * FROM Routine")
fun getRoutinesWithExercises(): List<RoutineWithExercises>
然后你就有了例程和练习。