0

I have my main activity actionbaractivity One where you can screenslide through some fragmets, on each fragment you have an imageView and a ListView where you can click any item and the image will change. Also in the menu options you have a button where you change to an almost exact activity: actiobbaractivity Two which also have this button to change to activity One What I'm able to do is to keep the image when sliding the fragments, but unable to keep the fragments state's through the change of activities.

For example I'm in activity One on fragment 3 with the image: "something". I click on the button to change to activity Two, I do things here and then, I click on the button to change to activity One and I want to see my fragment 3 with the image: "something" and not the default fragment 1 and default image

Im using ActionBarActivity, FragmentStatePagerAdapter and Fragment for each activity

Thanks for the help

1 Answers1

0

According to the Activity and Fragment lifecycles (http://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle and http://developer.android.com/guide/components/fragments.html#Lifecycle), the most reliable way of persisting states between activity/fragment changes is to use the default API for saving and restoring states:

When the activity/fragment is being dismissed (either because of a configuration change such as screen rotation or because the user requested to go to another activity/fragment), you can save its state in a Bundle object. When it is being created, you can restore its saved state, thus recreating a new instance exactly like the one the user left - so the user feels nothing has changed. This does not depend on the specific subclass of activity/fragment you are using.

I have implemented something like what you want: in my case, a fragment containing a menu with buttons that would each lead the user to another fragment containing a submenu with a "back" button. So if the user went from menu to submenu 1, then back to menu, then to submenu 2, then back to menu and finally again to submenu 1, I wanted that submenu 1 to appear just like the user has left it in the first time.

For that I have created:

1) an interface defining my submenu types, implemented by my activities so they could change between my submenus

2) a master generic class, which all my submenus would extend, that had a Bundle object to store their state

3) in my activities, I had an array of Bundle capable of storing one instance of each of my submenus (because I am only interested in restoring the last state, so I don't need more than one)

The interface (item 1):

    public interface SubmenusManager {
      public static enum Submenus {
        ROOTMENU, 
        SUBMENU1,
        SUBMENU2;

        private static final int size = Submenus.values().length;
        public static int size() {
          return size;
        }
        public static int getId(Submenus test) {
          switch(test) {
            case SUBMENU1: 
              return 1;

            case SUBMENU2: 
              return 2;

            case ROOTMENU: 
            default:    
              return 0;
          }
        }
      }

      public void cloneCurrentSubmenuState(Parcelable toOverwrite);
      public Bundle getLastStoredSubmenuState(Submenus submenu);
      public void setCurrentSubmenuTo(Submenus submenu);
    }

The generic class (item 2):

    public class MenuFragment extends Fragment {
      private Bundle menuData = new Bundle();
      public static String RESTORE_MAIN_OBJECT = "restore_main";

      public Bundle getMenuData() {
        return menuData;
      }
      public Bundle cloneMenuData() {
        return new Bundle(menuData);
      }

      public void setMenuData(Bundle menuData) {
        this.menuData = menuData;
      }
    }

One of the activities (item 3):

    public class ExampleAct extends FragmentActivity implements SubmenusManager {
      /**
      * instance variables
      */
      private MenuFragment mMenu; 
      private Bundle [] menuData; //  the Array of Bundles!
      private static final String CONTAINER = "parcelable_container";

      private static final String SUBMENU = "saved_submenu";
      private Submenus curSubmenu = Submenus.ROOTMENU; // the default state is the ROOTMENU
      private boolean restoreLastSavedState = false;

      @Override
      public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState == null) { // first time creating this activity
          menuData = new Bundle[Submenus.size()];
        } else { // this activity has a saved state from before
          // restore all the data from all the submenus
          menuData = (Bundle[]) savedInstanceState.getParcelableArray(CONTAINER);
          // restore the info about which is the current active submenu
          curSubmenu = (Submenus) savedInstanceState.getSerializable(SUBMENU);
        }

        buildMenuFragment(true);

        //(...) stuff
      }

      private void buildMenuFragment(boolean restoreState) {
        // (re)builds fragment inside menu. 
        // restoreState flags whether activity should look for
        // saved state data and restore it
        restoreLastSavedState = restoreState;
        switch(curSubmenu) { 
          // Eclipse warns you about which are the constants in your enum
          case ROOTMENU:
            mMenu = new FragmentRootMenu();
            break;
          case SUBMENU1:
            mMenu = new FragmentSubmenu1();
            break;
          case SUBMENU2:
            mMenu = new FragmentSubmenu2();
            break;
        }

        getSupportFragmentManager()
        .beginTransaction()
        .replace(R.id.menu_frame, mMenu)
        .commit();
      }

      @Override
      public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        outState.putSerializable(SUBMENU, curSubmenu);
        cloneCurrentSubmenuState(mMenu.getMenuData().
          getParcelable(MenuFragment.RESTORE_MAIN_OBJECT));
        outState.putParcelableArray(CONTAINER, menuData);

        // (...) stuff
      }

      @Override
      public void cloneCurrentSubmenuState(Parcelable toOverwrite) {
        if (menuData == null) menuData = new Bundle[Submenus.size()];

        if (toOverwrite != null) 
          mMenu.getMenuData().putParcelable(MenuFragment.RESTORE_MAIN_OBJECT, toOverwrite);

          menuData[Submenus.getId(curSubmenu)] = mMenu.cloneMenuData();
      }

      @Override
      public Bundle getLastStoredSubmenuState(Submenus forThisSubmenu) {
        return
          (menuData == null || !restoreLastSavedState) ? new Bundle() : menuData[Submenus.getId(forThisSubmenu)];
      }

      @Override
      public void setCurrentSubmenuTo(Submenus toThisSubmenu) {
        if (mMenu != null) {
          cloneCurrentSubmenuState(mMenu.getMenuData().
            getParcelable(MenuFragment.RESTORE_MAIN_OBJECT));
        }
        curSubmenu = toThisSubmenu;
        buildMenuFragment(true);
      }

One of the submenus (extension of item 2):

    public class FragmentSubmenu1 extends MenuFragment {
      @Override
      public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_submenu1, null);
      }

      @Override
      public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        init();
      }

      public void init() {
        // (...) stuff

        MyParcelableObject tmp = null; // MyParcelableObject is a class 
                                       // that implements Parcelable and stores 
                                       // relevant info to rebuild this menu 
                                       // from a saved state
        SubmenusManager m = (SubmenusManager) getActivity(); // remember activity implements SubmenusManager
        Bundle bnd = m.getLastStoredSubmenuState(SubmenusManager.Submenus.SUBMENU1);
        if (bnd != null) tmp = bnd.getParcelable(MenuFragment.RESTORE_MAIN_OBJECT);
        if (tmp == null) {
          tmp = new MyParcelableObject();
          tmp.buildFromScratch(); // initializes with default data
        }

        // back button
        Button backToMainMenu = (Button) getView().findViewById(R.id.submenu1_back);
        backToMainMenu.setOnClickListener(new OnClickListener() {
          @Override
          public void onClick(View v) {
            ((SubmenusManager) getActivity()).
              setCurrentSubmenuTo(SubmenusManager.Submenus.ROOTMENU);
          }
        });

        // (...) stuff
      }
    }

The Root menu (extension of item 2):

    public class FragmentRootMenu extends MenuFragment {
      View myView;

      @Override
      public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        myView = inflater.inflate(R.layout.fragment_rootmenu, null);
        return myView;
      }

      @Override
      public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        init();
      }

      public void init() {
        Button btnSubmenu1 = (Button) myView.findViewById(R.id.btn_call_submenu1);

        btnSubmenu1.setOnClickListener(new OnClickListener() {
          @Override
          public void onClick(View v) {
            ((SubmenusManager) getActivity()).
              setCurrentSubmenuTo(SubmenusManager.Submenus.SUBMENU1);
          }
        });

        Button btnSubmenu2 = (Button) myView.findViewById(R.id.btn_call_submenu2);

        btnSubmenu2.setOnClickListener(new OnClickListener() {
          @Override
          public void onClick(View v) {
            ((SubmenusManager) getActivity()).
              setCurrentSubmenuTo(SubmenusManager.Submenus.SUBMENU2);
          }
        });
      }
    }

For that to work between activities, all you need to do is pass that object that stores the last state of all fragments (in my case, that would be Bundle [] menuData) to the activity that is being called through its Intent; you would recover it the same way as my ExampleAct did in its onCreate(). You could also wrap that Bundle [] inside a custom Parcelable object (very similar to my example MyParcelableObject; inside that one I had stuff like HashMap) if using an array is a problem.

Here how to pass a Parcelable between activities: How to send an object from one Android Activity to another using Intents?

Community
  • 1
  • 1