带导航的 Kotlin - 如何将数据保留在前一个片段中

Kotlin with navigation - how to keep data in previous fragment

我正在使用导航图,我的片段 A 有一个 RecylerView。当我转到片段 B 时,我不想在返回片段 A 时丢失片段 A 中的数据。 我该怎么做?

分步解决。使用 ViewModel 更改片段后创建导航图并将 ListView 内容保存在片段中:

  1. build.gradle (Module: app) 添加:
dependencies { 
    ...

    def nav_version = "2.3.0"
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"

    def lifecycle_version = "2.2.0"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
}

android {
    ...

    kotlinOptions {
        jvmTarget = "1.8"
    }
}
  1. 创建两个片段(FragemntSecond可以为空):

FragemntStart:

<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Start Fragment"
        />

    <Button
        android:id="@+id/butGoTo2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Go To Second"
        />

    <Button
        android:id="@+id/butAddRandomValue"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Add random value"
        />

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        />

</androidx.appcompat.widget.LinearLayoutCompat>
  1. 创建导航图,添加两个片段并将它们连接到图上。在 MainActivity 添加 FragmentContainerView:
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/nav_host_fragment"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:defaultNavHost="true"
    app:navGraph="@navigation/navigation"
    />
  1. 创建一个 ViewModel class:
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

class CommonViewModel : ViewModel()
{
    // This list will keep all values for Your ListView
    private val _list: MutableLiveData<ArrayList<String>> = MutableLiveData()
    val list: LiveData<ArrayList<String>>
        get() = _list

    init
    {
        _list.value = ArrayList()
    }

    // function which will add new values to Your list
    fun addNewValue()
    {
        _list.value!!.add((0..100).random().toString())
        _list.value = _list.value //this will notify observers
    }
}
  1. 将 ViewModel 添加到 StartFragment:
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.Navigation
import kotlinx.android.synthetic.main.fragment_start.*

class StartFragment : Fragment()
{
    private lateinit var viewModel: CommonViewModel

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View?
    {
        viewModel = ViewModelProvider(requireActivity()).get(CommonViewModel::class.java) // init view model
        return inflater.inflate(R.layout.fragment_start, container, false)
    }

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

        butGoTo2.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_startFragment_to_secondFragment)) // set clickListener to navigate to second fragment
        butAddRandomValue.setOnClickListener { viewModel.addNewValue() } // listener for second button to add new value to ListView

        // set up observer. Every time You add sth to list, ListView will be updated
        viewModel.list.observe(viewLifecycleOwner, {
            listView.adapter = ArrayAdapter(
                requireContext(),
                android.R.layout.simple_list_item_1,
                it
            )
        })
    }
}

现在您可以转到第二个片段,当您 return 开始时您不会丢失任何数据。这是一个简单的示例,说明如何使用 ViewModel 将数据保存在 ListView 中。当您使用 RecyclerView 时,您可以创建一个扩展 ListAdapter 的适配器,并且在观察者中,您只需编写 adapter.submitList(it).


如果您想从 ViewModel 访问第二个片段中的数据(例如这个 list),您可以这样做:

class SecondFragment : Fragment()
{
    private val viewModel: CommonViewModel by activityViewModels() // retrieve the same view model
    
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View?
    {
        Log.d("MyTag", "${viewModel.list.value}") // here You can access list which is used in StartFragment 
        return inflater.inflate(R.layout.fragment_second, container, false)
    }
}