Issue
I'm using a navigation graph and my fragment A has a RecylerView. When I go to fragment B I don't want to lose the data from fragment A when I back there. How I can do this?
Solution
Step by step solution. Create a navigation graph and save ListView content in a fragment after changing fragment using ViewModel:
- To
build.gradle (Module: app)
add this:
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"
}
And
android {
...
kotlinOptions {
jvmTarget = "1.8"
}
}
- Create two fragments (
FragemntSecond
can be empty):
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>
- Create navigation graph, add both fragments and connect them on graph. To
MainActivity
addFragmentContainerView
:
<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"
/>
- Create a 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
}
}
- Add ViewModel to
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
)
})
}
}
Now You can go to the second fragment and when You return to Start You won't lose any data. This is a simple example of how You can use ViewModel to save data in ListView. When You are using RecyclerView You can create an adapter that extends ListAdapter and in the observer, You just have to write adapter.submitList(it)
.
If You want to access data in the second fragment (e.g. this list
) from ViewModel You can do it like this:
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)
}
}
Answered By - iknow
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.