Separe Back Stack para cada pestaña en Android usando Fragmentos

Resuelto mardah asked hace 54 años • 12 respuestas

Estoy intentando implementar pestañas para navegación en una aplicación de Android. Dado que TabActivity y ActivityGroup están en desuso, me gustaría implementarlos usando Fragmentos.

Sé cómo configurar un fragmento para cada pestaña y luego cambiar de fragmento cuando se hace clic en una pestaña. Pero, ¿cómo puedo tener una pila posterior separada para cada pestaña?

Por ejemplo, los Fragmentos A y B estarían en la Pestaña 1 y los Fragmentos C y D en la Pestaña 2. Cuando se inicia la aplicación, se muestra el Fragmento A y se selecciona la Pestaña 1. Entonces el Fragmento A podría ser reemplazado por el Fragmento B. Cuando se selecciona la pestaña 2, se debe mostrar el Fragmento C. Si se selecciona la pestaña 1, el Fragmento B debería mostrarse nuevamente. En este punto, debería ser posible utilizar el botón Atrás para mostrar el Fragmento A.

Además, es importante que el estado de cada pestaña se mantenga cuando se gira el dispositivo.

BR Martín

mardah avatar Jan 01 '70 08:01 mardah
Aceptado

Lea esto antes de usar esta solución.

Vaya, todavía no puedo creer que esta respuesta sea la que tiene más votos en este hilo. No siga ciegamente esta implementación. Escribí esta solución en 2012 (cuando era solo un novato en Android). Diez años después, puedo ver que hay un problema terrible con esta solución.

Estoy almacenando referencias concretas a fragmentos para implementar la pila de navegación. Es una práctica terrible y provocaría una pérdida de memoria. Deje que FragmentManager guarde la referencia a fragmentos. Simplemente almacene el identificador del fragmento si es necesario.

Mi respuesta se puede utilizar con la modificación anterior si es necesario. Pero no creo que necesitemos escribir una implementación de navegación multipila desde cero. Seguramente existe una solución mucho mejor para esto. No me gusta mucho Android hoy en día, así que no puedo señalar ninguno.

Mantengo la respuesta original en aras de la integridad.

Respuesta original

Llego terriblemente tarde a esta pregunta. Pero como este hilo ha sido muy informativo y útil para mí, pensé que sería mejor publicar mis dos peniques aquí.

Necesitaba un flujo de pantalla como este (un diseño minimalista con 2 pestañas y 2 vistas en cada pestaña),

tabA
    ->  ScreenA1, ScreenA2
tabB
    ->  ScreenB1, ScreenB2

Tenía los mismos requisitos en el pasado y lo hice usando TabActivityGroup(que también estaba obsoleto en ese momento) y Actividades. Esta vez quería usar Fragmentos.

Así es como lo hice.

1. Cree una clase de fragmento base

public class BaseFragment extends Fragment {
    AppMainTabActivity mActivity;
  
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mActivity = (AppMainTabActivity) this.getActivity();
    }
  
    public void onBackPressed(){
    }
  
    public void onActivityResult(int requestCode, int resultCode, Intent data){
    }
}

Todos los fragmentos de su aplicación pueden ampliar esta clase base. Si desea utilizar fragmentos especiales como este, ListFragmenttambién debe crear una clase base para eso. Tendrás claro el uso de onBackPressed()y onActivityResult()si lees la publicación en su totalidad.

2. Cree algunos identificadores de pestañas, accesibles desde cualquier lugar del proyecto.

public class AppConstants{
    public static final String TAB_A  = "tab_a_identifier";
    public static final String TAB_B  = "tab_b_identifier";
   
    //Your other constants, if you have them..
}

nada que explicar aqui..

3. Ok, Actividad de la pestaña principal: revise los comentarios en el código.

public class AppMainFragmentActivity extends FragmentActivity{
    /* Your Tab host */
    private TabHost mTabHost;
  
    /* A HashMap of stacks, where we use tab identifier as keys..*/
    private HashMap<String, Stack<Fragment>> mStacks;
  
    /*Save current tabs identifier in this..*/
    private String mCurrentTab;
  
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.app_main_tab_fragment_layout);
    
        /*  
         *  Navigation stacks for each tab gets created.. 
         *  tab identifier is used as key to get respective stack for each tab
         */
        mStacks             =   new HashMap<String, Stack<Fragment>>();
        mStacks.put(AppConstants.TAB_A, new Stack<Fragment>());
        mStacks.put(AppConstants.TAB_B, new Stack<Fragment>());
    
        mTabHost                =   (TabHost)findViewById(android.R.id.tabhost);
        mTabHost.setOnTabChangedListener(listener);
        mTabHost.setup();
    
        initializeTabs();
    }
  
  
    private View createTabView(final int id) {
        View view = LayoutInflater.from(this).inflate(R.layout.tabs_icon, null);
        ImageView imageView =   (ImageView) view.findViewById(R.id.tab_icon);
        imageView.setImageDrawable(getResources().getDrawable(id));
        return view;
    }
  
    public void initializeTabs(){
        /* Setup your tab icons and content views.. Nothing special in this..*/
        TabHost.TabSpec spec    =   mTabHost.newTabSpec(AppConstants.TAB_A);
        mTabHost.setCurrentTab(-3);
        spec.setContent(new TabHost.TabContentFactory() {
            public View createTabContent(String tag) {
                return findViewById(R.id.realtabcontent);
            }
        });
        spec.setIndicator(createTabView(R.drawable.tab_home_state_btn));
        mTabHost.addTab(spec);

  
        spec                    =   mTabHost.newTabSpec(AppConstants.TAB_B);
        spec.setContent(new TabHost.TabContentFactory() {
            public View createTabContent(String tag) {
                return findViewById(R.id.realtabcontent);
            }
        });
        spec.setIndicator(createTabView(R.drawable.tab_status_state_btn));
        mTabHost.addTab(spec);
    }
  
  
    /*Comes here when user switch tab, or we do programmatically*/
    TabHost.OnTabChangeListener listener    =   new TabHost.OnTabChangeListener() {
      public void onTabChanged(String tabId) {
        /*Set current tab..*/
        mCurrentTab                     =   tabId;
        
        if(mStacks.get(tabId).size() == 0){
          /*
           *    First time this tab is selected. So add first fragment of that tab.
           *    Dont need animation, so that argument is false.
           *    We are adding a new fragment which is not present in stack. So add to stack is true.
           */
          if(tabId.equals(AppConstants.TAB_A)){
            pushFragments(tabId, new AppTabAFirstFragment(), false,true);
          }else if(tabId.equals(AppConstants.TAB_B)){
            pushFragments(tabId, new AppTabBFirstFragment(), false,true);
          }
        }else {
          /*
           *    We are switching tabs, and target tab is already has atleast one fragment. 
           *    No need of animation, no need of stack pushing. Just show the target fragment
           */
          pushFragments(tabId, mStacks.get(tabId).lastElement(), false,false);
        }
      }
    };


    /* Might be useful if we want to switch tab programmatically, from inside any of the fragment.*/
    public void setCurrentTab(int val){
          mTabHost.setCurrentTab(val);
    }
  
  
    /* 
     *      To add fragment to a tab. 
     *  tag             ->  Tab identifier
     *  fragment        ->  Fragment to show, in tab identified by tag
     *  shouldAnimate   ->  should animate transaction. false when we switch tabs, or adding first fragment to a tab
     *                      true when when we are pushing more fragment into navigation stack. 
     *  shouldAdd       ->  Should add to fragment navigation stack (mStacks.get(tag)). false when we are switching tabs (except for the first time)
     *                      true in all other cases.
     */
    public void pushFragments(String tag, Fragment fragment,boolean shouldAnimate, boolean shouldAdd){
      if(shouldAdd)
          mStacks.get(tag).push(fragment);
      FragmentManager   manager         =   getSupportFragmentManager();
      FragmentTransaction ft            =   manager.beginTransaction();
      if(shouldAnimate)
          ft.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left);
      ft.replace(R.id.realtabcontent, fragment);
      ft.commit();
    }
  
  
    public void popFragments(){
      /*    
       *    Select the second last fragment in current tab's stack.. 
       *    which will be shown after the fragment transaction given below 
       */
      Fragment fragment             =   mStacks.get(mCurrentTab).elementAt(mStacks.get(mCurrentTab).size() - 2);
      
      /*pop current fragment from stack.. */
      mStacks.get(mCurrentTab).pop();
      
      /* We have the target fragment in hand.. Just show it.. Show a standard navigation animation*/
      FragmentManager   manager         =   getSupportFragmentManager();
      FragmentTransaction ft            =   manager.beginTransaction();
      ft.setCustomAnimations(R.anim.slide_in_left, R.anim.slide_out_right);
      ft.replace(R.id.realtabcontent, fragment);
      ft.commit();
    }   


    @Override
    public void onBackPressed() {
        if(mStacks.get(mCurrentTab).size() == 1){
          // We are already showing first fragment of current tab, so when back pressed, we will finish this activity..
          finish();
          return;
        }
      
        /*  Each fragment represent a screen in application (at least in my requirement, just like an activity used to represent a screen). So if I want to do any particular action
         *  when back button is pressed, I can do that inside the fragment itself. For this I used AppBaseFragment, so that each fragment can override onBackPressed() or onActivityResult()
         *  kind of events, and activity can pass it to them. Make sure just do your non navigation (popping) logic in fragment, since popping of fragment is done here itself.
         */
        ((AppBaseFragment)mStacks.get(mCurrentTab).lastElement()).onBackPressed();
        
        /* Goto previous fragment in navigation stack of this tab */
            popFragments();
    }


    /*
     *   Imagine if you wanted to get an image selected using ImagePicker intent to the fragment. Ofcourse I could have created a public function
     *  in that fragment, and called it from the activity. But couldn't resist myself.
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if(mStacks.get(mCurrentTab).size() == 0){
            return;
        }

        /*Now current fragment on screen gets onActivityResult callback..*/
        mStacks.get(mCurrentTab).lastElement().onActivityResult(requestCode, resultCode, data);
    }
}

4. app_main_tab_fragment_layout.xml (por si alguien está interesado).

<?xml version="1.0" encoding="utf-8"?>
<TabHost
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/tabhost"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">

        <FrameLayout
            android:id="@android:id/tabcontent"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_weight="0"/>

        <FrameLayout
            android:id="@+android:id/realtabcontent"
            android:layout_width="fill_parent"
            android:layout_height="0dp"
            android:layout_weight="1"/>
            
        <TabWidget
            android:id="@android:id/tabs"
            android:orientation="horizontal"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="0"/>
                        
    </LinearLayout>
</TabHost>

5. AppTabAFirstFragment.java (Primer fragmento en la pestaña A, similar para todas las pestañas)

public class AppTabAFragment extends BaseFragment {
    private Button mGotoButton;
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View view       =   inflater.inflate(R.layout.fragment_one_layout, container, false);
        
        mGoToButton =   (Button) view.findViewById(R.id.goto_button);
        mGoToButton.setOnClickListener(listener);
    
        return view;
    }

    private OnClickListener listener        =   new View.OnClickListener(){
        @Override
        public void onClick(View v){
            /* Go to next fragment in navigation stack*/
            mActivity.pushFragments(AppConstants.TAB_A, new AppTabAFragment2(),true,true);
        }
    }
}

Puede que esta no sea la forma más pulida y correcta. Pero funcionó muy bien en mi caso. Además, sólo tenía este requisito en modo retrato. Nunca tuve que usar este código en un proyecto que admitiera ambas orientaciones. Así que no puedo decir qué tipo de desafíos enfrento allí...

Si alguien quiere un proyecto completo, he enviado un proyecto de muestra a github .

Krishnabhadra avatar Oct 17 '2012 06:10 Krishnabhadra

Tuvimos que implementar exactamente el mismo comportamiento que usted describe para una aplicación recientemente. Las pantallas y el flujo general de la aplicación ya estaban definidos, así que tuvimos que seguir con ello (es un clon de la aplicación iOS...). Afortunadamente, logramos deshacernos de los botones de retroceso en pantalla :)

Hackeamos la solución usando una combinación de TabActivity, FragmentActivities (estábamos usando la biblioteca de soporte para fragmentos) y Fragments. En retrospectiva, estoy bastante seguro de que no fue la mejor decisión arquitectónica, pero logramos que todo funcionara. Si tuviera que hacerlo de nuevo, probablemente intentaría hacer una solución más basada en actividades (sin fragmentos), o intentaría tener solo una Actividad para las pestañas y dejaría que el resto fueran vistas (que creo que son mucho más). reutilizables que las actividades en general).

Entonces los requisitos eran tener algunas pestañas y pantallas encajables en cada pestaña:

tab 1
  screen 1 -> screen 2 -> screen 3
tab 2
  screen 4
tab 3
  screen 5 -> 6

etc...

Entonces digamos: el usuario comienza en la pestaña 1, navega de la pantalla 1 a la pantalla 2 y luego a la pantalla 3, luego cambia a la pestaña 3 y navega de la pantalla 4 a la 6; si volvió a la pestaña 1, debería ver la pantalla 3 nuevamente y si presionó Atrás debería regresar a la pantalla 2; De regreso y está en la pantalla 1; cambia a la pestaña 3 y estará en la pantalla 6 nuevamente.

La actividad principal de la aplicación es MainTabActivity, que extiende TabActivity. Cada pestaña está asociada con una actividad, digamos ActivityInTab1, 2 y 3. Y luego cada pantalla será un fragmento:

MainTabActivity
  ActivityInTab1
    Fragment1 -> Fragment2 -> Fragment3
  ActivityInTab2
    Fragment4
  ActivityInTab3
    Fragment5 -> Fragment6

Cada ActivityInTab contiene solo un fragmento a la vez y sabe cómo reemplazar un fragmento por otro (prácticamente lo mismo que ActvityGroup). Lo bueno es que de esta manera es bastante fácil mantener pilas traseras separadas para cada pestaña.

La funcionalidad para cada ActivityInTab era bastante la misma: saber cómo navegar de un fragmento a otro y mantener una pila de actividades, así que lo pusimos en una clase base. Llamémoslo simplemente ActivityInTab:

abstract class ActivityInTab extends FragmentActivity { // FragmentActivity is just Activity for the support library.

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_in_tab);
    }

    /**
     * Navigates to a new fragment, which is added in the fragment container
     * view.
     * 
     * @param newFragment
     */
    protected void navigateTo(Fragment newFragment) {
        FragmentManager manager = getSupportFragmentManager();
        FragmentTransaction ft = manager.beginTransaction();

        ft.replace(R.id.content, newFragment);

        // Add this transaction to the back stack, so when the user presses back,
        // it rollbacks.
        ft.addToBackStack(null);
        ft.commit();
    }

}

Activity_in_tab.xml es solo esto:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/content"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:isScrollContainer="true">
</RelativeLayout>

Como puede ver, el diseño de vista para cada pestaña era el mismo. Esto se debe a que es solo un FrameLayout llamado contenido que contendrá cada fragmento. Los fragmentos son los que tienen la vista de cada pantalla.

Solo para obtener puntos extra, también agregamos un pequeño código para mostrar un cuadro de diálogo de confirmación cuando el usuario presiona Atrás y no hay más fragmentos a los que regresar:

// In ActivityInTab.java...
@Override
public void onBackPressed() {
    FragmentManager manager = getSupportFragmentManager();
    if (manager.getBackStackEntryCount() > 0) {
        // If there are back-stack entries, leave the FragmentActivity
        // implementation take care of them.
        super.onBackPressed();
    } else {
        // Otherwise, ask user if he wants to leave :)
        showExitDialog();
    }
}

Esa es prácticamente la configuración. Como puede ver, cada FragmentActivity (o simplemente Actividad en Android >3) se encarga de todo el back-stacking con su propio FragmentManager.

Una actividad como ActivityInTab1 será realmente simple, solo mostrará su primer fragmento (es decir, la pantalla):

public class ActivityInTab1 extends ActivityInTab {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        navigateTo(new Fragment1());
    }
}

Luego, si un fragmento necesita navegar a otro fragmento, tiene que hacer un casting un poco desagradable... pero no es tan malo:

// In Fragment1.java for example...
// Need to navigate to Fragment2.
((ActivityIntab) getActivity()).navigateTo(new Fragment2());

Así que eso es todo. Estoy bastante seguro de que esta no es una solución muy canónica (y sobre todo no muy buena), por lo que me gustaría preguntar a los desarrolladores de Android experimentados cuál sería un mejor enfoque para lograr esta funcionalidad y, si no es así, "cómo se hace". "hecho" en Android, le agradecería que me indicara algún enlace o material que explique cuál es la forma en que Android aborda esto (pestañas, pantallas anidadas en pestañas, etc.). Siéntete libre de destrozar esta respuesta en los comentarios :)

Una señal de que esta solución no es muy buena es que recientemente tuve que agregar algunas funciones de navegación a la aplicación. Algún botón extraño que debería llevar al usuario de una pestaña a otra y a una pantalla anidada. Hacer eso programáticamente fue un dolor de cabeza, debido a los problemas de quién sabe quién y a lidiar con cuándo se crean instancias e inicializan los fragmentos y actividades. Creo que habría sido mucho más fácil si esas pantallas y pestañas fueran en realidad solo Vistas.


Finalmente, si necesita sobrevivir a los cambios de orientación, es importante que sus fragmentos se creen usando setArguments/getArguments. Si configuras variables de instancia en los constructores de tus fragmentos, estarás jodido. Pero, afortunadamente, eso es muy fácil de solucionar: simplemente guarde todo en setArguments en el constructor y luego recupere esas cosas con getArguments en onCreate para usarlas.

epidemian avatar Sep 20 '2011 04:09 epidemian

Actualmente, el marco no hará esto automáticamente. Necesitará crear y administrar sus propias pilas para cada pestaña.

Para ser honesto, esto parece algo realmente cuestionable. No puedo imaginar que resulte en una interfaz de usuario decente: si la tecla Atrás va a hacer cosas diferentes dependiendo de la pestaña en la que me encuentre, especialmente si la tecla Atrás también tiene su comportamiento normal de cerrar toda la actividad cuando está en la parte superior de la pila... suena desagradable.

Si está intentando crear algo como una interfaz de usuario de navegador web, obtener una UX que sea natural para el usuario implicará muchos ajustes sutiles de comportamiento según el contexto, por lo que definitivamente necesitará hacer su propia pila de respaldo. gestión en lugar de depender de alguna implementación predeterminada en el marco. Por ejemplo, intente prestar atención a cómo la tecla Atrás interactúa con el navegador estándar en las distintas formas en que puede entrar y salir de él. (Cada "ventana" del navegador es esencialmente una pestaña).

hackbod avatar Aug 17 '2011 07:08 hackbod

Esto se puede lograr fácilmente con ChildFragmentManager

Aquí hay una publicación sobre esto con el proyecto asociado. echar un vistazo,

http://tausiq.wordpress.com/2014/06/06/android-multiple-fragments-stack-in-each-viewpager-tab/

tausiq avatar Jun 17 '2014 02:06 tausiq

Almacenar fuertes referencias a fragmentos no es la forma correcta.

FragmentManager proporciona putFragment(Bundle, String, Fragment)y saveFragmentInstanceState(Fragment).

Cualquiera de los dos es suficiente para implementar un backstack.


Usando putFragment, en lugar de reemplazar un Fragmento, separa el antiguo y agrega el nuevo. Esto es lo que hace el marco con una transacción de reemplazo que se agrega al backstack. putFragmentalmacena un índice de la lista actual de Fragmentos activos y el marco guarda esos Fragmentos durante los cambios de orientación.

La segunda forma, usando saveFragmentInstanceState, guarda todo el estado del fragmento en un paquete, lo que le permite eliminarlo realmente, en lugar de separarlo. El uso de este enfoque hace que la pila posterior sea más fácil de manipular, ya que puede extraer un Fragmento cuando lo desee.


Utilicé el segundo método para este caso de uso:

SignInFragment ----> SignUpFragment ---> ChooseBTDeviceFragment
               \                          /
                \------------------------/

No quiero que el usuario regrese a la pantalla de Registro, desde la tercera, presionando el botón Atrás. También invierto animaciones entre ellas (usando onCreateAnimation), por lo que las soluciones hacky no funcionarán, al menos sin que el usuario se dé cuenta claramente de que algo no está bien.

Este es un caso de uso válido para un backstack personalizado, que hace lo que el usuario espera...

private static final String STATE_BACKSTACK = "SetupActivity.STATE_BACKSTACK";

private MyBackStack mBackStack;

@Override
protected void onCreate(Bundle state) {
    super.onCreate(state);

    if (state == null) {
        mBackStack = new MyBackStack();

        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction tr = fm.beginTransaction();
        tr.add(R.id.act_base_frg_container, new SignInFragment());
        tr.commit();
    } else {
        mBackStack = state.getParcelable(STATE_BACKSTACK);
    }
}

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putParcelable(STATE_BACKSTACK, mBackStack);
}

private void showFragment(Fragment frg, boolean addOldToBackStack) {
    final FragmentManager fm = getSupportFragmentManager();
    final Fragment oldFrg = fm.findFragmentById(R.id.act_base_frg_container);

    FragmentTransaction tr = fm.beginTransaction();
    tr.replace(R.id.act_base_frg_container, frg);
    // This is async, the fragment will only be removed after this returns
    tr.commit();

    if (addOldToBackStack) {
        mBackStack.push(fm, oldFrg);
    }
}

@Override
public void onBackPressed() {
    MyBackStackEntry entry;
    if ((entry = mBackStack.pop()) != null) {
        Fragment frg = entry.recreate(this);

        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction tr = fm.beginTransaction();
        tr.replace(R.id.act_base_frg_container, frg);
        tr.commit();

        // Pop it now, like the framework implementation.
        fm.executePendingTransactions();
    } else {
        super.onBackPressed();
    }
}

public class MyBackStack implements Parcelable {

    private final List<MyBackStackEntry> mList;

    public MyBackStack() {
        mList = new ArrayList<MyBackStackEntry>(4);
    }

    public void push(FragmentManager fm, Fragment frg) {
        push(MyBackStackEntry.newEntry(fm, frg);
    }

    public void push(MyBackStackEntry entry) {
        if (entry == null) {
            throw new NullPointerException();
        }
        mList.add(entry);
    }

    public MyBackStackEntry pop() {
        int idx = mList.size() - 1;
        return (idx != -1) ? mList.remove(idx) : null;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        final int len = mList.size();
        dest.writeInt(len);
        for (int i = 0; i < len; i++) {
            // MyBackStackEntry's class is final, theres no
            // need to use writeParcelable
            mList.get(i).writeToParcel(dest, flags);
        }
    }

    protected MyBackStack(Parcel in) {
        int len = in.readInt();
        List<MyBackStackEntry> list = new ArrayList<MyBackStackEntry>(len);
        for (int i = 0; i < len; i++) {
            list.add(MyBackStackEntry.CREATOR.createFromParcel(in));
        }
        mList = list;
    }

    public static final Parcelable.Creator<MyBackStack> CREATOR =
        new Parcelable.Creator<MyBackStack>() {

            @Override
            public MyBackStack createFromParcel(Parcel in) {
                return new MyBackStack(in);
            }

            @Override
            public MyBackStack[] newArray(int size) {
                return new MyBackStack[size];
            }
    };
}

public final class MyBackStackEntry implements Parcelable {

    public final String fname;
    public final Fragment.SavedState state;
    public final Bundle arguments;

    public MyBackStackEntry(String clazz, 
            Fragment.SavedState state,
            Bundle args) {
        this.fname = clazz;
        this.state = state;
        this.arguments = args;
    }

    public static MyBackStackEntry newEntry(FragmentManager fm, Fragment frg) {
        final Fragment.SavedState state = fm.saveFragmentInstanceState(frg);
        final String name = frg.getClass().getName();
        final Bundle args = frg.getArguments();
        return new MyBackStackEntry(name, state, args);
    }

    public Fragment recreate(Context ctx) {
        Fragment frg = Fragment.instantiate(ctx, fname);
        frg.setInitialSavedState(state);
        frg.setArguments(arguments);
        return frg;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(fname);
        dest.writeBundle(arguments);

        if (state == null) {
            dest.writeInt(-1);
        } else if (state.getClass() == Fragment.SavedState.class) {
            dest.writeInt(0);
            state.writeToParcel(dest, flags);
        } else {
            dest.writeInt(1);
            dest.writeParcelable(state, flags);
        }
    }

    protected MyBackStackEntry(Parcel in) {
        final ClassLoader loader = getClass().getClassLoader();
        fname = in.readString();
        arguments = in.readBundle(loader);

        switch (in.readInt()) {
            case -1:
                state = null;
                break;
            case 0:
                state = Fragment.SavedState.CREATOR.createFromParcel(in);
                break;
            case 1:
                state = in.readParcelable(loader);
                break;
            default:
                throw new IllegalStateException();
        }
    }

    public static final Parcelable.Creator<MyBackStackEntry> CREATOR =
        new Parcelable.Creator<MyBackStackEntry>() {

            @Override
            public MyBackStackEntry createFromParcel(Parcel in) {
                return new MyBackStackEntry(in);
            }

            @Override
            public MyBackStackEntry[] newArray(int size) {
                return new MyBackStackEntry[size];
            }
    };
}
sergio91pt avatar Apr 19 '2014 12:04 sergio91pt