Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to manage UI state and the back stack in a single/dual-pane layout

I'm having trouble wrapping my head around how to properly manage Fragments 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.

example

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 Fragments 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?

like image 813
quietmint Avatar asked Sep 14 '12 20:09

quietmint


2 Answers

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.

like image 93
quietmint Avatar answered Nov 17 '22 11:11

quietmint


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:

  • State A: portrait, showing the list fragment.
  • State B: portrait, showing the details fragment with the list fragment on the back stack.
  • State C: landscape, showing the list fragment on the left and a blank space on the right.
  • State D: landscape, showing both the list fragment and the details fragment side by side.

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.

like image 4
oats Avatar answered Nov 17 '22 09:11

oats