10

I'm using the compatibility package v4 in my project and I'm having an issue with keeping a Fragment around after it's removed from view. I have my Activity displaying 2 fragments...a menu frame on the left, content pane on the right. The menu frame has 3 different menus (Fragments) that can be displayed.

This is how I'm replacing the menu Fragment:

public void showMenuFragment( Fragment fragment, String tag ) {

        showFragment( R.id.menu_frame, fragment, tag, false);
        setLastMenuPushed( tag );
    }

protected void showFragment( int resId, Fragment fragment, String tag, boolean addToBackStack ) {

        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction transaction = fragmentManager.beginTransaction();

        if ( fragmentManager.findFragmentByTag( tag ) != null && fragment.isAdded() ) {
            transaction.remove( fragment );
        }

        transaction.replace( resId, fragment, tag ).setTransition( FragmentTransaction.TRANSIT_FRAGMENT_OPEN ).setBreadCrumbShortTitle( tag );

        if ( addToBackStack ) {
            transaction.addToBackStack( tag );
        }

        transaction.commit();
    }

The menu Fragments require data to be loaded before it can be displayed, so I show a loading spinner. Once it's loaded the first time, I want to not have to ever load it a second time unless the Activity is finished.

When I call showMenuFragment(...) I try to pass in a Fragment using FragmentManager.findFragmentByTag(String tag) but it's always null, so I have to make a new Fragment every time I want to show a different menu.

My question is, how do I keep these Fragments around after they are replaced by other menus? I want to avoid keeping manual references to the Fragments because when the device is rotated, the instance of my Activity class is destroyed and I would lose those references.

EDIT: To put in much simpler terms, I want the FragmentManager to keep track of previous Fragments without having to add them to the back stack. I don't want to hit the back button and see the previous menu shown.

Jason Robinson
  • 31,005
  • 19
  • 77
  • 131

2 Answers2

15

Just figured it out. Instead of removing Fragments when I add new ones, I just hide them, then show them when I want to display them again. Here's my modified methods:

public void showMenuFragment( Fragment fragment, String tag, boolean addToBackStack ) {

        showFragment( R.id.menu_frame, fragment, tag, getLastMenuPushed(), addToBackStack );
        setLastMenuPushed( tag );
}

protected void showFragment( int resId, Fragment fragment, String tag, String lastTag, boolean addToBackStack ) {

        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction transaction = fragmentManager.beginTransaction();

        if ( lastTag != null ) {
            Fragment lastFragment = fragmentManager.findFragmentByTag( lastTag );
            if ( lastFragment != null ) {
                transaction.hide( lastFragment );
            }
        }

        if ( fragment.isAdded() ) {
            transaction.show( fragment );
        }
        else {
            transaction.add( resId, fragment, tag ).setBreadCrumbShortTitle( tag );
        }

        if ( addToBackStack ) {
            transaction.addToBackStack( tag );
        }

        transaction.commit();
}
Jason Robinson
  • 31,005
  • 19
  • 77
  • 131
  • 3
    Looking back on this, I think the better solution was to "detach" the Fragment from the Activity and keep a hard reference to it in the class. – Jason Robinson Sep 25 '12 at 15:43
  • What would be the advantage of detach vs hide ? Does destroying the view makes a real difference ? Do you have any good way to restore it without reloading the whole fragment ? – ben Jan 25 '13 at 18:28
  • @ben Detach would remove the view hierarchy from the screen, which is a more eco-friendly approach. Most Views do a good job of saving their instance state when removed from a Fragment, so I wouldn't see a big deal there when reattaching. – Jason Robinson Jan 25 '13 at 19:05
  • attach detach call requires api level 13 is there any alternative way. – madan V Feb 25 '14 at 09:35
  • 2
    Actually, it appears "detach() the old - add() the new" does the same thing as replace(), i.e. restarts the Fragment's lifecycle with a null instance state Bundle, so that wouldn't help. – ugo Mar 01 '14 at 04:56
0

I'm not sure if this is possible. FragmentManager takes care ONLY on Fragments on his BackStack. But you can workaround the back-button-pressed problem if you let your FragmentActivity implement onBackButtonPressed and let custom behavior take place. You could delegate them to your Fragments, or just show another Fragment depending on your App-logic

mmBs
  • 8,421
  • 6
  • 38
  • 46
Rafael T
  • 15,401
  • 15
  • 83
  • 144