8

我正在使用 Android Navigation 组件制作应用程序。但是我遇到了一个非常基本的问题,这可能会导致我的应用程序的整个开发出现问题。

情景

我有这个片段,onViewCreated我正在从我的视图模型中观察一个字段。

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    viewModel = ViewModelProviders.of(this).get(EventDetailsViewModel::class.java)
    viewModel.init(context!!,eventId)

    viewModel.onEventDetailsUpdated().observe(this, Observer {
        setEventDetails(it)
    })

}

setEventDetails方法中,我用数据设置了recyclerviews。

问题

这个片段是一个带有卷轴的长片段。假设我向下滚动很长一段路,然后单击一个按钮,将我带到另一个片段。

但是当我回到这个片段时,它再次将我带到顶部并完成它在第一次加载时所做的一切。这可能令人不安。这是一种重新创建整个片段而不是保持其旧状态。

我试过的

我搜索了很多问题。并经历了这个 Github 查询这个 SO 问题另一个 Git ......但我无法解决我的问题。

请帮助,在此先感谢。

4

4 回答 4

1

是的,只要您向前导航到另一个片段,片段的视图就会被破坏。

RecyclerView 的滚动位置应该会自动恢复,即使创建了新的 RecyclerView 实例并设置了新的 Adapter 实例,只要您使用与以前相同的数据集设置所有内容。此外,您需要在第一次布局传递之前执行此操作。

这意味着您需要旧数据并且需要立即准备好它(没有异步加载!)。ViewModelProvider 应该返回相同的 ViewModel 实例。该 ViewModel 包含您应该能够同步获取并在 UI 上显示的数据。确保重构您的 viewModel.init 方法 - 如果数据已经存在,您不想进行 API 调用以防万一。一个简单的布尔值isInitialized可以在这里工作,或者您甚至可以检查 LiveData 是否为空。

observe此外,在调用LiveData. onViewCreated 可以为同一个片段调用多次(每次你向前和向后导航时!) - 所以observe每次都会被调用。您的 Fragment 将被多次订阅LiveData。这意味着您将多次获得事件(每个订阅一次)。这也可能导致 RecyclerView 状态恢复出现问题。您的订阅与您传递的生命周期所有者相关联。您传递了与 Fragment 生命周期相关联的 Fragment 生命周期所有者。您要做的是传递 Fragment 视图的生命周期所有者,因此每当视图被销毁时,订阅就会被清除,并且您只有 1 个订阅,并且只有在 Fragment 的视图处于活动状态时。为此,您可以使用getViewLifecycleOwner而不是this.

于 2020-03-02T09:35:20.263 回答
1

您需要依靠 ViewModel 来恢复片段状态,因为 ViewModel 不会在片段更改时被破坏。在您的 viewModel 中,创建一个变量 listState

class HomeViewModel : ViewModel() {
    var listState: Parcelable? = null
}

然后在你的片段中使用下面的代码


class HomeFragment : Fragment() {
    private val viewModel by navGraphViewModels<HomeViewModel>(R.id.mobile_navigation)

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        if (viewModel.listState != null) {
            list.layoutManager?.onRestoreInstanceState(viewModel.listState)
            viewModel.listState = null
        }else{
           //load data normally
    }

    override fun onDestroyView() {
        super.onDestroyView()
        viewModel.listState = list.layoutManager?.onSaveInstanceState()
    }
}
于 2020-04-16T16:16:24.170 回答
0

您不必每次都初始化视图模型。只需在初始化之前检查 null。不知道 kotlin,仍然会是这样的:

if(viewModel == null){
    viewModel = ViewModelProviders.of(this).get(EventDetailsViewModel::class.java)
    viewModel.init(context!!,eventId)
}
于 2020-03-02T09:13:13.177 回答
-1

尝试将此代码放在您第一次调用片段的位置。

ft = fm.beginTransaction();
 ft.replace(R.id.main_fragment, yourSearchFragment, "searchFragment");
 ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
 ft.commit();

当回到片段时

ft = fm.beginTransaction();
 ft.hide(getFragmentManager().findFragmentByTag("searchFragment"));
 ft.add(R.id.main_fragment, yourDetailfragment);
 ft.addToBackStack(null);
 ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
 ft.commit();
于 2019-07-26T06:20:50.590 回答