I'm having trouble wrapping my head around how to properly manage Fragment
s in a typical list-detail design pattern.
My layout is working fine and is structured like this for the landscape view (dual pane):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout ... >
<FrameLayout android:id="@+id/list" ... />
<FrameLayout android:id="@+id/container" ... />
</LinearLayout>
And like this for the portrait view (single pane):
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout android:id="@+id/container" ... />
I also have a refs.xml
in res/values-w600dp/
to make sure the appropriate layout is loaded based on the screen width of the device.
When a list item is selected, the landscape view should show the list and the details side-by-side, while the portrait view should show only the details fullscreen. When nothing has been selected, the landscape view should show the left on the left and blank space on the right, while the portrait view should show only the list fullscreen.
However, here's where I'm having trouble:
Do I use a "main" Activity
for the list + optional details layout, and another "details" Activity
for the portrait, details-only layout? This seems to be what the Fragments guide example does. I've gotten this approah to work generally, but how do I maintain the state of my details fragment when the orientation changes?
Consider the opposite case shown above -- rotating from portrait to landscape while the details are visible means the details Activity
would need to be finish()
ed so the main Activity
could display the two-pane layout, but this also destroys the details fragment and its savedInstanceState
. If the user has typed information into the EditText
fields in the details fragment, then rotates the device, how do I preserve the entire UI state of the details fragment when I add it to the main (two-pane) Activity
?
Do I use only one Activity
and manage the Fragment
s myself? This allows me to retain the state of the details UI when the orientation changes (because Android takes care of this automatically), but it gets messy when it comes to the back stack.
Consider the case in the image -- rotating from landscape to portrait when the details are visible should show show the details single-pane, but how do I properly manage the back stack and ActionBar
home icon to show the list single-pane? Rotating back to landscape would also need to undo whatever back stack manipulation I did previously, as both fragments will be visible at once.
After some more searching, I found two similar questions (Switch from dual pane to single pane on orientation change maintaining fragment stack and Retain Fragment state between Activities), but my situation is slightly different because I'm not trying to retrofit this functionality but best plan for it up front. What am I missing? Surely Android can manage both the back stack (as in the multiple Activity
case) and the UI state (as in the single Activity
case) at once, right?
I solved this by going the multiple activities route. See my answer to "Retain Fragment state between Activities".
The key was using FragmentManager.saveFragmentInstanceState()
to save the state of the fragment's UI and Fragment.setInitialSavedState()
to restore it when instantiating the same fragment in another activity.
For my purposes, I found that I needed to save the UI state in two cases. I created a helper method public void saveState()
inside my fragment and call this from the fragment's own onPause()
method as well as from my "main" activity immediately prior to using replace()
to attach a new instance of the fragment to the dual-pane layout.
I'm still interested in know if the back stack manipulation required for this type of single/dual-pane layout scenario is possible or if the single activity approach would require overriding onBackPressed
and simulating a back stack manually.
It is possible to use only one Activity, but you'll have to manage the backstack when in portrait orientation yourself.
As you describe the behaviour of the app, there could be 4 states:
Imagine you are on state D (with both fragments, let's call them FA and FB), and you change the orientation of the phone. The following state should be the state B, but the problem is that your portrait layout only has one FrameLayout
. Using the following as the single pane layout:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout android:id="@+id/list" ... />
Android will recreate FA and will have it attached to that FrameLayout
. FB, on the other hand, will also be recreated but will not have a container associated, so it won't show up. At this point you could create another fragment, FB2 with the same contents as FB, and use it to replace FA. You will have FA-FB2 in your backstack and FB hidden.
If you change the orientation again (expecting the state D), you can use onSaveInstanceState()
to pop FB2 from the backstack with popBackStack()
, thus having again FA and FB. Before removing FB2, you may also want to copy its content to FB. When the app is again in state D, FB will have its FrameLayout.
There would be a few other transitions and situations that you would have to take care of, but the main idea would be that.
It's not a very elegant solution, so I am sure that it has to be a better, maybe more efficient (without replicating Fragments for example) way to solve this problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With