防止我的片段和列表在每次显示时都被创建

Prevent my fragment and list from being created every time they are displayed

我第一次在我的应用程序中将数据绑定和片段与导航组件结合使用。在导航和加载我的列表方面一切正常。我遇到的问题是:

当我在列表中并单击其中一项时,我导航到该项目的详细信息片段。但是当我return到列表(onBack)时,片段和列表再次加载,它们被重新创建。

我想要实现的是 不会重新创建片段和列表。它们保持相同的状态,这样当我从细节return开始时,我可以从我离开的地方继续浏览我的列表。

我的代码:

片段

 class ListFragment : Fragment() {

    private lateinit var viewDataBinding: FragmentListBinding
    private lateinit var adapter: ListAdapter

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        viewDataBinding = FragmentListBinding.inflate(inflater, container, false).apply {
            viewmodel = ViewModelProviders.of(this@ListFragment).get(ListViewModel::class.java)
            lifecycleOwner = viewLifecycleOwner
        }
        return viewDataBinding.root
    }

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

        setupAdapter()
        setupObservers()
    }

    private fun setupObservers() {
        viewDataBinding.viewmodel?.listLive?.observe(viewLifecycleOwner, Observer {
            adapter.updateList(it)
        })

        viewDataBinding.viewmodel?.toastMessage?.observe(viewLifecycleOwner, Observer {
            activity?.longToast(it)
        })
    }

    private fun setupAdapter() {
        val viewModel = viewDataBinding.viewmodel

        if (viewModel != null) {
            adapter = ListAdapter(viewDataBinding.viewmodel!!)
            val layoutManager = LinearLayoutManager(activity)
            list_rv.layoutManager = layoutManager
            list_rv.adapter = adapter
        }
    }

    override fun onResume() {
        super.onResume()
        viewDataBinding.viewmodel?.fetchList()
    }
}

片段布局

<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">

<data>

    <import type="android.view.View" />

    <variable
        name="viewmodel"
        type="app.example.list.ListViewModel" />
</data>

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

        <androidx.recyclerview.widget.RecyclerView
         android:id="@+id/list_rv"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:visibility="@{safeUnbox(viewmodel.dataLoading) ? View.GONE : View.VISIBLE}" />
          
</RelativeLayout>

查看模型

class ListViewModel : BaseViewModel() {
val listLive = MutableLiveData<List<Item>>()

fun fetchList() {
    dataLoading.value = true
    ItemRepository.getInstance().getList() { isSuccess, response ->
        dataLoading.value = false

        if (isSuccess) {
            listLive.value = response?.items
            empty.value = false
        } else {
            empty.value = true
        }
    }
}
}

我正在使用导航在列表和项目之间导航

itemView.findNavController().navigate(R.id.action_listFragment_to_detailFragment)

由于以下代码,您每次都会重新创建回收列表 -

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

    setupAdapter()     //you are setting adapter every time view is getting created
    setupObservers()
}

当您使用导航架构进行导航时,每次导航到目的地时,前一个片段将被停止()并在返回时重新创建。因为截至目前导航组件没有后台 issue

但是使用 ViewModel 的全部意义在于不保存片段状态并让 ViewModel 保留数据。您正在使用视图模型,但是您不能每次都在回收视图上设置适配器。

当您从视图模型中获取列表时,只需通知回收视图列表。

您永远不应该在 onResume() 等生命周期方法中重新加载数据 - 这就是为什么每次返回片段时数据都会重新加载。

相反,您应该在 ViewModel 的 init 块中只调用一次 fetchList() - 这确保它只运行一次:

class ListViewModel : BaseViewModel() {
     val listLive = MutableLiveData<List<Item>>()

      init {
          fetchList()
      }

      fun fetchList() {
           dataLoading.value = true
           ItemRepository.getInstance().getList() { isSuccess, response ->
                dataLoading.value = false

                if (isSuccess) {
                     listLive.value = response?.items
                     empty.value = false
                } else {
                     empty.value = true
                }
           }
      }
 }

不清楚您的 getList() 方法是如何工作的。现在的写法对于单个回调通常是正确的。如果您的 getList() 实际上返回了一个值流,随着基础数据的变化继续传递回调,那么您必须确保在 onCleared() 中注销该回调(当 ViewModel 被销毁时的回调)。