【问题标题】:ViewModel architecture dillemaViewModel 架构困境
【发布时间】:2020-05-04 19:21:53
【问题描述】:

我有三个片段,每个片段都有自己的 RecyclerView。如果用户从第一个片段中选择了一条路径,则根据选择将他带到具有相关课程的下一个片段,然后选择课程并转到具有相关课程的最后一个片段。所以每个 RecyclerView 都依赖于前一个 RecyclerView 的数据。

我对所有 Fragment 都使用了共享 ViewModel,但效果不佳,因为它们都会在 Activity 启动时被初始化,因为数据检索功能在 ViewModel 中初始化的 Repository 中。

我为它们中的每一个都使用了一个单独的 ViewModel,范围仅限于 Activity,但也不能很好地工作,因为只有在 Activity 开始时才会检索数据,而不会再次检索数据,因此在片段之间来回导航时相同数据和项目出现。

如果我仅将单个 ViewModel 限定为片段,则后一个片段无法访问前一个片段所需的数据。

如何使用 ViewModel 架构解决这个难题?还是我应该只使用 SafeArgs 来传递数据?

CourseViewModel

class CourseViewModel(app: Application): AndroidViewModel(app) {

    private val courseDataRepository = CourseRepository(app)
    val courseData = courseDataRepository.courseData
    val selectedCourse = MutableLiveData<Course>()
}

CourseFragment.kt

class CourseFragment : Fragment(),
    CourseRecyclerAdapter.CourseItemListener {

    private lateinit var viewModel: CourseViewModel
    private lateinit var recyclerView: RecyclerView
    private lateinit var navController: NavController

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_course, container, false)
        recyclerView = view.findViewById(R.id.courseRecyclerView)
        navController = Navigation.findNavController(requireActivity(), R.id.nav_host )

        viewModel = ViewModelProvider(this).get(CourseViewModel::class.java)
        viewModel.courseData.observe(viewLifecycleOwner, Observer {
            val adapter =
                CourseRecyclerAdapter(
                    requireContext(),
                    it,
                    this
                )
            recyclerView.adapter = adapter
        } )
        return view
    }

这是我放弃之前的共享模型:

class SharedViewModel(app: Application): AndroidViewModel(app) {

    private val pathDataRepository = PathRepository(app)
    val pathData = pathDataRepository.pathData
    val selectedPath = MutableLiveData<Path>()

    private val courseDataRepository = CourseRepository(app)
    val courseData = courseDataRepository.courseData
    val selectedCourse = MutableLiveData<Course>()

    private val lessonDataRepository = LessonRepository(app)
    val lessonData = lessonDataRepository.lessonData
    val selectedLesson = MutableLiveData<Lesson>()
}

【问题讨论】:

  • 更好的方法是使用单独的视图模型,只需将 id 从一个片段传递到另一个片段,然后从本地数据库或服务器获取必要的数据。 @ReazMurshed 的回答很好,解决了您共享视图模型的问题,但您的实际问题是关注点分离。在未来,您的视图模型将成为一个上帝视图模型,在主屏幕、列表和细节等松散相关的屏幕中执行各种操作。主片段和列表片段之间唯一的共同点是所选列表的 id。而list和detail的共同点就是课程的id
  • @sonnet ,我目前正在使用单独的视图模型。正确地,他们之间唯一共享的是 ID,但中间 CourseFragments 从服务器检索数据和图像,我想将其传递给最后一个 Detail 片段
  • 我可以让 CourseFragment 检索数据,也可以让 Detail 片段检索相同的数据,这是我想避免的事情
  • 使用 android room 或 sqldelight 将数据存储在 sqlite 数据库中。任何像样的图像加载库都会缓存图像。只有第一次从服务器下载图像时,从下一次它会从应用程序的缓存目录加载图像。您还可以根据您的要求使此图像缓存无效。总的来说,这是一个更好的方法。范围为根/父片段的片段之间的 SharedViewModel 有其用例,但这不是 imo 之一。
  • @sonnet,sqlite 数据库的想法听起来很不错,我绝对认为它是最好的方法,谢谢。

标签: android android-fragments kotlin android-viewmodel


【解决方案1】:

如果我理解正确,您可以将片段构建为具有以下结构。

MainFragment
 - CourseFragment 
 - DetailsFragment 

我们的想法是让MainFragment 作为父级,并使用MainFragment 中的getChildFragmentManager() 启动其他两个片段,如下所示。

getChildFragmentManager().beginTransaction()
    .replace(R.id.fragment_container, courseFragment)
    .addToBackStack(null).commit();

现在为MainFragment 创建一个ViewModel,并将ViewModel 的范围限定为MainFragment 的生命周期。这将作为CourseFragmentDetailsFragment 的共享ViewModel 工作。您可以访问范围为您的父片段(即MainFragment)的ViewModel,如下所示。

SharedViewModel vm = ViewModelProviders.of(getParentFragment())
                         .get(SharedViewModel.class);

【讨论】:

  • Murshedm 我已经有一个用于 MainFragment 的共享视图模型,我的主要问题是反过来,我无法从 CourseFragment 共享数据到 DetailFragment
  • 您可以在使用该共享视图模型的用户之间共享数据。
  • 但是在从CourseFragment 访问ViewModel 时,您将其范围限定为这个特定的片段——我认为您没有使用共享的片段。使用viewModel = ViewModelProvider(getParentFragment()).get(CourseViewModel::class.java)。在这种情况下,您还需要将 MainFragment 设置为这两个子片段的父级。
  • 当然,如果您遇到问题,请告诉我。编码愉快!
  • 我用片段交易更新了答案。您需要通过MainFragment 执行此操作。
【解决方案2】:

按照@sonnet 的建议,我通过将数据存储在 Room 数据库中,然后在用户输入/重新输入任何片段时检索它,从而限制了远程数据检索,从而解决了这个问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-04-21
    • 2017-03-30
    • 2011-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多