带导航的 Kotlin - 如何将数据保留在前一个片段中
Kotlin with navigation - how to keep data in previous fragment
我正在使用导航图,我的片段 A 有一个 RecylerView。当我转到片段 B 时,我不想在返回片段 A 时丢失片段 A 中的数据。
我该怎么做?
分步解决。使用 ViewModel 更改片段后创建导航图并将 ListView 内容保存在片段中:
- 向
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"
}
}
- 创建两个片段(
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>
- 创建导航图,添加两个片段并将它们连接到图上。在
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"
/>
- 创建一个 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
}
}
- 将 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)
}
}
我正在使用导航图,我的片段 A 有一个 RecylerView。当我转到片段 B 时,我不想在返回片段 A 时丢失片段 A 中的数据。 我该怎么做?
分步解决。使用 ViewModel 更改片段后创建导航图并将 ListView 内容保存在片段中:
- 向
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"
}
}
- 创建两个片段(
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>
- 创建导航图,添加两个片段并将它们连接到图上。在
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"
/>
- 创建一个 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
}
}
- 将 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)
}
}