从数据库中导入的数据未设置

发布于 2025-02-04 03:33:21 字数 4030 浏览 2 评论 0原文

我正在制作与图像类似的屏幕。

提前从Room db中获取数据集,并为每个选项卡设置数据集。

每个选项卡为fragment,并在recyclerview中显示数据。

每个选项卡都包含不同的数据,因此我将选项卡设置为livedata in viewModel并观察它。

因此,每当选项卡更改时,目标是从数据库中获取每个选项卡的数据,并在recyclerview中设置它。

但是,即使我导入数据,也没有在recyclerview中设置它。

我认为即使在调试数据时,数据也很顺利。

这不是适配器问题。

我想念什么?


Workoutlist

@Entity
data class WorkoutList(
    @PrimaryKey(autoGenerate = true)
    val id: Long = 0,
    val chest: List<String>,
    val back: List<String>,
    val leg: List<String>,
    val shoulder: List<String>,
    val biceps: List<String>,
    val triceps: List<String>,
    val abs: List<String>
)

ViewModel

class WorkoutListViewModel(application: Application) : AndroidViewModel(application){
    private var _part :MutableLiveData<BodyPart> = MutableLiveData()
    private var result : List<String> = listOf()

    private val workoutDao = WorkoutListDatabase.getDatabase(application).workoutListDao()
    private val workoutListRepo = WorkoutListRepository(workoutDao)
    
    val part = _part

    fun setList(part : BodyPart) : List<String> {
        _part.value = part

        viewModelScope.launch(Dispatchers.IO){
            result = workoutListRepo.getWorkoutList(part)
        }
        return result
    }
}

存储库

class WorkoutListRepository(private val workoutListDao: WorkoutListDao) {
    suspend fun getWorkoutList(part: BodyPart) : List<String> {
        val partList = workoutListDao.getWorkoutList()

        return when(part) {
            is BodyPart.Chest -> partList.chest
            is BodyPart.Back -> partList.back
            is BodyPart.Leg -> partList.leg
            is BodyPart.Shoulder -> partList.shoulder
            is BodyPart.Biceps -> partList.biceps
            is BodyPart.Triceps -> partList.triceps
            is BodyPart.Abs -> partList.abs
        }
    }
}

fragment

class WorkoutListTabPageFragment : Fragment() {
    private var _binding : FragmentWorkoutListTabPageBinding? = null
    private val binding get() = _binding!!
    private lateinit var adapter: WorkoutListAdapter
    private lateinit var part: BodyPart

    private val viewModel: WorkoutListViewModel by viewModels()

    companion object {
        @JvmStatic
        fun newInstance(part: BodyPart) =
            WorkoutListTabPageFragment().apply {
                arguments = Bundle().apply {
                    putParcelable("part", part)
                }
            }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let { bundle ->
            part = bundle.getParcelable("part") ?: throw NullPointerException("No BodyPart Object")
        }
    }

    override fun onCreateView(inflater: LayoutInflater,
                              container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        _binding = FragmentWorkoutListTabPageBinding.inflate(inflater, container, false)
        binding.apply {
            adapter = WorkoutListAdapter()
            rv.adapter = adapter
        }

        val result = viewModel.setList(part)

        // Set data whenever tab changes
        viewModel.part.observe(viewLifecycleOwner) { _ ->
//            val result = viewModel.setList(part)
            adapter.addItems(result)
        }

        return binding.root
    }
}     viewModel.part.observe(viewLifecycleOwner) { _ ->
            adapter.addItems(result)
        }

        return binding.root
    }
}

enter image description here

I'm making a screen similar to the image.

The data set in advance is taken from the Room DB and the data is set for each tab.

Each tab is a fragment and displays the data in a RecyclerView.

Each tab contains different data, so i set Tab to LiveData in ViewModel and observe it.

Therefore, whenever tabs change, the goal is to get the data for each tab from the database and set it in the RecyclerView.

However, even if I import the data, it is not set in RecyclerView.

I think the data comes in well even when I debug it.

This is not an adapter issue.

What am I missing?


WorkoutList

@Entity
data class WorkoutList(
    @PrimaryKey(autoGenerate = true)
    val id: Long = 0,
    val chest: List<String>,
    val back: List<String>,
    val leg: List<String>,
    val shoulder: List<String>,
    val biceps: List<String>,
    val triceps: List<String>,
    val abs: List<String>
)

ViewModel

class WorkoutListViewModel(application: Application) : AndroidViewModel(application){
    private var _part :MutableLiveData<BodyPart> = MutableLiveData()
    private var result : List<String> = listOf()

    private val workoutDao = WorkoutListDatabase.getDatabase(application).workoutListDao()
    private val workoutListRepo = WorkoutListRepository(workoutDao)
    
    val part = _part

    fun setList(part : BodyPart) : List<String> {
        _part.value = part

        viewModelScope.launch(Dispatchers.IO){
            result = workoutListRepo.getWorkoutList(part)
        }
        return result
    }
}

Repository

class WorkoutListRepository(private val workoutListDao: WorkoutListDao) {
    suspend fun getWorkoutList(part: BodyPart) : List<String> {
        val partList = workoutListDao.getWorkoutList()

        return when(part) {
            is BodyPart.Chest -> partList.chest
            is BodyPart.Back -> partList.back
            is BodyPart.Leg -> partList.leg
            is BodyPart.Shoulder -> partList.shoulder
            is BodyPart.Biceps -> partList.biceps
            is BodyPart.Triceps -> partList.triceps
            is BodyPart.Abs -> partList.abs
        }
    }
}

Fragment

class WorkoutListTabPageFragment : Fragment() {
    private var _binding : FragmentWorkoutListTabPageBinding? = null
    private val binding get() = _binding!!
    private lateinit var adapter: WorkoutListAdapter
    private lateinit var part: BodyPart

    private val viewModel: WorkoutListViewModel by viewModels()

    companion object {
        @JvmStatic
        fun newInstance(part: BodyPart) =
            WorkoutListTabPageFragment().apply {
                arguments = Bundle().apply {
                    putParcelable("part", part)
                }
            }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let { bundle ->
            part = bundle.getParcelable("part") ?: throw NullPointerException("No BodyPart Object")
        }
    }

    override fun onCreateView(inflater: LayoutInflater,
                              container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        _binding = FragmentWorkoutListTabPageBinding.inflate(inflater, container, false)
        binding.apply {
            adapter = WorkoutListAdapter()
            rv.adapter = adapter
        }

        val result = viewModel.setList(part)

        // Set data whenever tab changes
        viewModel.part.observe(viewLifecycleOwner) { _ ->
//            val result = viewModel.setList(part)
            adapter.addItems(result)
        }

        return binding.root
    }
}     viewModel.part.observe(viewLifecycleOwner) { _ ->
            adapter.addItems(result)
        }

        return binding.root
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

贱人配狗天长地久 2025-02-11 03:33:21

您看到的问题是,在setList中,您在IO线程上启动异步的coroutine以获取列表,但是您实际上并没有等待该coroutine运行,而只需立即返回空列表。

修复的一种方法是观察一个包含列表的寿命对象,而不是观察零件。然后,当异步任务完成时
您可以将检索到的数据发布到该Livedata。然后在视图模型中看起来像这样,

class WorkoutListViewModel(application: Application) : AndroidViewModel(application) {

    private val _list = MutableLiveData<List<String>>()
    val list: LiveData<List<String>>
        get() = _list

    // "part" does not need to be a member of the view model 
    // based on the code you shared, but if you wanted it
    // to be you could do it like this, then
    // call "viewModel.part = part" in "onCreateView". It does not need
    // to be LiveData if it's only ever set from the Fragment directly.
    //var part: BodyPart = BodyPart.Chest
    
       
    // calling getList STARTS the async process, but the function
    // does not return anything
    fun getList(part: BodyPart) {
        viewModelScope.launch(Dispatchers.IO){
            val result = workoutListRepo.getWorkoutList(part)
            _list.postValue(result)
        }
    }
}

然后在片段increateview中观察列表,当值更改时,您将它们添加到适配器中。如果值可能会发生多次变化,则可能需要清除适配器,然后再添加观察者内部的项目。


override fun onCreateView(inflater: LayoutInflater,
                          container: ViewGroup?,
                          savedInstanceState: Bundle?): View? {
    //...

    // Set data whenever new data is posted
    viewModel.list.observe(viewLifecycleOwner) { result ->
        adapter.addItems(result)
    }
    
    // Start the async process of retrieving the list, when retrieved
    // it will be posted to the live data and trigger the observer
    viewModel.getList(part)

    return binding.root
}
    

note 文档当前仅建议在increateview中仅夸大视图并在<代码中进行所有其他设置和初始化> onviewCreated - 我将其保留在问题中以保持一致性。

The problem you are seeing is that in setList you start an asynchronous coroutine on the IO thread to get the list, but then you don't actually wait for that coroutine to run but just return the empty list immediately.

One way to fix that would be to observe a LiveData object containing the list, instead of observing the part. Then, when the asynchronous task is complete
you can post the retrieved data to that LiveData. That would look like this in the view model

class WorkoutListViewModel(application: Application) : AndroidViewModel(application) {

    private val _list = MutableLiveData<List<String>>()
    val list: LiveData<List<String>>
        get() = _list

    // "part" does not need to be a member of the view model 
    // based on the code you shared, but if you wanted it
    // to be you could do it like this, then
    // call "viewModel.part = part" in "onCreateView". It does not need
    // to be LiveData if it's only ever set from the Fragment directly.
    //var part: BodyPart = BodyPart.Chest
    
       
    // calling getList STARTS the async process, but the function
    // does not return anything
    fun getList(part: BodyPart) {
        viewModelScope.launch(Dispatchers.IO){
            val result = workoutListRepo.getWorkoutList(part)
            _list.postValue(result)
        }
    }
}

Then in the fragment onCreateView you observe the list, and when the values change you add them to the adapter. If the values may change several times you may need to clear the adapter before adding the items inside the observer.


override fun onCreateView(inflater: LayoutInflater,
                          container: ViewGroup?,
                          savedInstanceState: Bundle?): View? {
    //...

    // Set data whenever new data is posted
    viewModel.list.observe(viewLifecycleOwner) { result ->
        adapter.addItems(result)
    }
    
    // Start the async process of retrieving the list, when retrieved
    // it will be posted to the live data and trigger the observer
    viewModel.getList(part)

    return binding.root
}
    

Note: The documentation currently recommends only inflating views in onCreateView and doing all other setup and initialization in onViewCreated - I kept it how you had it in your question for consistency.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文