0

So I have an activity with DrawerLayout. I can switch to Fragment1 which contains TabLayout with three other fragments (NestedFragment1, NestedFragment2, NestedFragment3). I want to be able to save data in each NestedFragments since I make API calls inside them. I tried to override onSaveInstanceState and save the data but SavedInstanceState was always null.

Fragment1 code:

    class Fragment1 : Fragment() {
        private lateinit var viewPager: ViewPager
        private lateinit var tabLayout: TabLayout
        private lateinit var pagerAdapter: PagerAdapter

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            pagerAdapter = ForecastPagerAdapter(childFragmentManager)

        }

        override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                                  savedInstanceState: Bundle?): View? {
            val view = inflater.inflate(R.layout.fragment_forecast, container, false)
            viewPager = view.findViewById(R.id.view_pager)
            viewPager.offscreenPageLimit = 3
            viewPager.adapter = pagerAdapter
            tabLayout = view.findViewById(R.id.tabs)
            tabLayout.setupWithViewPager(viewPager)
            return view
        }
}

Example of nested fragment code:

    class NestedFragment : Fragment() {

        private lateinit var recyclerView: RecyclerView
        private lateinit var recyclerViewAdapter: HourForecastAdapter
        private lateinit var swipeRefreshLayout: SwipeRefreshLayout

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

            recyclerViewAdapter = HourForecastAdapter()


        }

        override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
            val view = inflater!!.inflate(R.layout.fragment_day_forecast, container, false)

            swipeRefreshLayout = view.findViewById(R.id.swipe_refresh_layout_hour)
            swipeRefreshLayout.apply {
                this.setOnRefreshListener {
                    performQuery()
                }
            }
            recyclerView = view.findViewById(R.id.recycler_view_hour_forecast)
            recyclerView.adapter = recyclerViewAdapter
            recyclerView.layoutManager = LinearLayoutManager(activity)


            return view
        }

 fun performQuery() {
        // ...

                    recyclerViewAdapter.forecastList = result.hourly.data
                    recyclerViewAdapter.notifyDataSetChanged()

    }
}

I want to save forecastList (List) which is returned by performQuery method. Every time I switch to Fragment 1 all data from nested fragments is gone.

This is the code used for switching fragments:

navigationView.setNavigationItemSelectedListener {
                var fragment = when (it.itemId) {
                    R.id.nav_map -> Fragment0()
                    R.id.nav_forecast -> Fragment1()
                    R.id.nav_app_info -> Fragment2()
                    else -> Fragment1()
                }

                replaceFragment()

                it.isChecked = true
                drawerLayout.closeDrawers()
                true
            }
        }

Edit: added function for fragment replacement:

fun replaceFragment(fragment: android.support.v4.app.Fragment){
        val fragmentName = fragment::class.java.simpleName
        val isFragmentInBackStack = fragmentManager.popBackStackImmediate(fragmentName, 0)
        if (!isFragmentInBackStack) {
            fragmentManager.beginTransaction()
                    .replace(R.id.main_activity_frame, fragment)
                    .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
                    .addToBackStack(fragmentName)
                    .commit()
        }
    }
dawzaw
  • 419
  • 6
  • 17
  • Please show a [mcve]. It sounds like you need to create a new Android Studio project with an activity and and just enough fragments to illustrate what you are trying to do. – Code-Apprentice May 17 '18 at 09:30

2 Answers2

0

First, you have to call .addToBackstack(null) before you commit your transaction. This will affect your back-button also. See #documentation:

Add this transaction to the back stack. This means that the transaction will be remembered after it is committed, and will reverse its operation when later popped off the stack.

Why this works this way? Your application doesn't known you want to open old fragment instead of creating new one (because you actually asked for creating new fragment). Also, you have to tell your application - "Ey, you! Don't create new fragment, but get the old one from backstack if there is any".

You can check how to do it fe. here: How to resume Fragment from BackStack if exists

PS: It's good practice to creating fragment not by constructor, but by static method "getInstance(): Fragment".

Cililing
  • 4,303
  • 1
  • 17
  • 35
  • I updated my code to change the way of adding the fragments. But the problem still remains. – dawzaw May 17 '18 at 10:13
  • And you get fragment from backstack, and you don't create new one? – Cililing May 17 '18 at 10:17
  • No. I log this and fragment manager replaces it instead of getting the fragment from the backstack. – dawzaw May 17 '18 at 10:23
  • So the problem appears to be popBackStackImmediate always returning false. But I have no idea why. The backstack can only increase its size ... – dawzaw May 17 '18 at 11:40
0

So I managed to solve this problem by using fragment "caching":

fun cacheFragment(fragment: android.support.v4.app.Fragment) {
        Log.d("Caching", "Working with ${fragment.javaClass.simpleName}")
        var findFragment = supportFragmentManager.findFragmentByTag(fragment.javaClass.simpleName)
        if (findFragment == null) {
            Log.d("Caching", "Creating new instance of ${fragment.javaClass.simpleName}")
            findFragment = fragment.javaClass.newInstance()
        }
        supportFragmentManager.beginTransaction()
                .replace(R.id.main_activity_frame, findFragment, fragment.javaClass.simpleName)
                .addToBackStack(null)
                .commit()
    }

When a user selects an item from nav bar, we check if the fragment already exists in the back stack and then we can reuse it.

dawzaw
  • 419
  • 6
  • 17