【发布时间】:2020-06-20 11:55:28
【问题描述】:
我的目标
我正在尝试创建一个包含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)
应用数据库
@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 的 Fragment 中,我正在观察 RoutineViewModel 并将例程发送到 RecyclerView 适配器。
private fun sendRoutinesToAdapter(adapter: RoutineAdapter) {
routineViewModel.allRoutines.observe(viewLifecycleOwner, Observer { routines ->
routines?.let { adapter.setRoutines(it) }
})
}
我的问题
因为ExerciseRoutineJoin 在其表中没有Exercise 名称,所以我正在创建ExerciseRoutineJoins 的MutableMap,其中Routine 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 返回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)
}
}
}
【问题讨论】:
-
您能具体说明一下您的数据库结构吗?如果您只需要一个 List
在您的表中,您可以创建一个 typeConverter ,这将使事情变得更容易,而无需使用关系。
标签: android kotlin android-recyclerview android-room android-livedata