Fragmentos de onResume de pila posterior


Estoy usando el paquete de compatibilidad para usar Fragmentos con Android 2.2. Al usar fragmentos y agregar transiciones entre ellos al backstack, me gustaría lograr el mismo comportamiento de onResume de una actividad, es decir, cuando un fragmento se lleva a "primer plano" (visible para el usuario) después de salir del backstack, me gustaría que se activara algún tipo de devolución de llamada dentro del fragmento (para realizar ciertos cambios en un recurso de interfaz de usuario compartido, por ejemplo).

Vi que hay no hay devolución de llamada incorporada dentro del marco de fragment. ¿existe una buena práctica para lograrlo?

Author: JJD, 2011-06-28

12 answers

Por falta de una solución mejor, tengo esto funcionando para mí: Supongamos que tengo 1 actividad (MyActivity) y pocos fragmentos que se reemplazan entre sí (solo uno es visible a la vez).

En MyActivity, agregue este oyente:

getSupportFragmentManager().addOnBackStackChangedListener(getListener());

(Como puedes ver estoy usando el paquete de compatibilidad).

Implementación de GetListener:

private OnBackStackChangedListener getListener()
    {
        OnBackStackChangedListener result = new OnBackStackChangedListener()
        {
            public void onBackStackChanged() 
            {                   
                FragmentManager manager = getSupportFragmentManager();

                if (manager != null)
                {
                    MyFragment currFrag = (MyFragment) manager.findFragmentById(R.id.fragmentItem);

                    currFrag.onFragmentResume();
                }                   
            }
        };

        return result;
    }

MyFragment.onFragmentResume() se llamará después de presionar un "Back". algunas advertencias sin embargo:

  1. Supone que has añadido todos transacciones a la backstack (usando FragmentTransaction.addToBackStack())
  2. Se activará en cada pila cambiar (puede almacenar otras cosas en la pila posterior, como la animación) es posible que reciba varias llamadas para el la misma instancia de fragmento.
 105
Author: oriharel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-07 13:09:29

He cambiado un poco la solución sugerida. Funciona mejor para mí así:

private OnBackStackChangedListener getListener() {
    OnBackStackChangedListener result = new OnBackStackChangedListener() {
        public void onBackStackChanged() {
            FragmentManager manager = getSupportFragmentManager();
            if (manager != null) {
                int backStackEntryCount = manager.getBackStackEntryCount();
                if (backStackEntryCount == 0) {
                    finish();
                }
                Fragment fragment = manager.getFragments()
                                           .get(backStackEntryCount - 1);
                fragment.onResume();
            }
        }
    };
    return result;
}
 31
Author: Brotoo25,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-05 23:44:55

La siguiente sección de Desarrolladores de Android describe un mecanismo de comunicación Que Crea devoluciones de llamada de eventos a la actividad. Para citar una línea de él:

Una buena manera de hacerlo es definir una interfaz de devolución de llamada dentro del fragmento y requerir que la actividad del host la implemente. Cuando la actividad recibe una devolución de llamada a través de la interfaz, puede compartir la información con otros fragmentos en el diseño según sea necesario.

Editar: Fragmento tiene un onStart(...) que se invoca cuando el fragmento es visible para el usuario. Del mismo modo un onResume(...) cuando es visible y se está ejecutando activamente. Estos están vinculados a sus contrapartes de actividad. En resumen: use onResume()

 4
Author: PJL,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-13 01:06:54

Después de un popStackBack() puedes usar la siguiente devolución de llamada: onHiddenChanged(boolean hidden) dentro de tu fragmento

 3
Author: user2230304,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-16 15:35:02

Si un fragmento se pone en backstack, Android simplemente destruye su vista. La instancia de fragmento en sí no se mata. Una forma sencilla de comenzar debe escuchar el evento onViewCreated, y poner allí la lógica " onResume ()".

boolean fragmentAlreadyLoaded = false;
    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onViewCreated(view, savedInstanceState);

        if (savedInstanceState == null && !fragmentAlreadyLoaded) {
            fragmentAlreadyLoaded = true;

            // Code placed here will be executed once
        }

        //Code placed here will be executed even when the fragment comes from backstack
    }
 2
Author: Franjo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-01 12:38:48

Un poco mejorado y envuelto en una solución de administrador.

Cosas a tener en cuenta. FragmentManager no es un singleton, administra solo Fragmentos dentro de la Actividad, por lo que en cada actividad será nueva. Además, esta solución hasta ahora no tiene en cuenta ViewPager que llama al método setUserVisibleHint() ayudando a controlar la visibilidad de los Fragmentos.

Siéntase libre de usar las siguientes clases cuando trate este problema (usa Dagger2 injection). Llamar Actividad:

//inject FragmentBackstackStateManager instance to myFragmentBackstackStateManager
FragmentManager fragmentManager = getSupportFragmentManager(); 
myFragmentBackstackStateManager.apply(fragmentManager);

FragmentBackstackStateManager.java:

@Singleton
public class FragmentBackstackStateManager {

    private FragmentManager fragmentManager;

    @Inject
    public FragmentBackstackStateManager() {
    }

    private BackstackCallback backstackCallbackImpl = new BackstackCallback() {
        @Override
        public void onFragmentPushed(Fragment parentFragment) {
            parentFragment.onPause();
        }

        @Override
        public void onFragmentPopped(Fragment parentFragment) {
            parentFragment.onResume();
        }
    };

    public FragmentBackstackChangeListenerImpl getListener() {
        return new FragmentBackstackChangeListenerImpl(fragmentManager, backstackCallbackImpl);
    }

    public void apply(FragmentManager fragmentManager) {
        this.fragmentManager = fragmentManager;
        fragmentManager.addOnBackStackChangedListener(getListener());
    }
}

FragmentBackstackChangeListenerImpl.java:

public class FragmentBackstackChangeListenerImpl implements FragmentManager.OnBackStackChangedListener {

    private int lastBackStackEntryCount = 0;
    private final FragmentManager fragmentManager;
    private final BackstackCallback backstackChangeListener;

    public FragmentBackstackChangeListenerImpl(FragmentManager fragmentManager, BackstackCallback backstackChangeListener) {
        this.fragmentManager = fragmentManager;
        this.backstackChangeListener = backstackChangeListener;
        lastBackStackEntryCount = fragmentManager.getBackStackEntryCount();
    }

    private boolean wasPushed(int backStackEntryCount) {
        return lastBackStackEntryCount < backStackEntryCount;
    }

    private boolean wasPopped(int backStackEntryCount) {
        return lastBackStackEntryCount > backStackEntryCount;
    }

    private boolean haveFragments() {
        List<Fragment> fragmentList = fragmentManager.getFragments();
        return fragmentList != null && !fragmentList.isEmpty();
    }


    /**
     * If we push a fragment to backstack then parent would be the one before => size - 2
     * If we pop a fragment from backstack logically it should be the last fragment in the list, but in Android popping a fragment just makes list entry null keeping list size intact, thus it's also size - 2
     *
     * @return fragment that is parent to the one that is pushed to or popped from back stack
     */
    private Fragment getParentFragment() {
        List<Fragment> fragmentList = fragmentManager.getFragments();
        return fragmentList.get(Math.max(0, fragmentList.size() - 2));
    }

    @Override
    public void onBackStackChanged() {
        int currentBackStackEntryCount = fragmentManager.getBackStackEntryCount();
        if (haveFragments()) {
            Fragment parentFragment = getParentFragment();

            //will be null if was just popped and was last in the stack
            if (parentFragment != null) {
                if (wasPushed(currentBackStackEntryCount)) {
                    backstackChangeListener.onFragmentPushed(parentFragment);
                } else if (wasPopped(currentBackStackEntryCount)) {
                    backstackChangeListener.onFragmentPopped(parentFragment);
                }
            }
        }

        lastBackStackEntryCount = currentBackStackEntryCount;
    }
}

BackstackCallback.java:

public interface BackstackCallback {
    void onFragmentPushed(Fragment parentFragment);

    void onFragmentPopped(Fragment parentFragment);
}
 2
Author: AAverin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-22 09:17:29

En mi actividad onCreate ()

getSupportFragmentManager().addOnBackStackChangedListener(getListener());

Use este método para capturar un fragmento específico y llamar a onResume()

private FragmentManager.OnBackStackChangedListener getListener()
    {
        FragmentManager.OnBackStackChangedListener result = new FragmentManager.OnBackStackChangedListener()
        {
            public void onBackStackChanged()
            {
                Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
                if (currentFragment instanceof YOURFRAGMENT) {
                    currentFragment.onResume();
                }
            }
        };

        return result;
    }
 1
Author: Quan Nguyen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-17 11:42:08

Esta es la respuesta correcta que puede llamar a onResume() siempre que el fragmento esté adjunto a la actividad. Alternativamente, puede usar onAttach y onDetach

 0
Author: iamlukeyb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-01 12:49:56

OnResume() para el fragmento funciona bien...

public class listBook extends Fragment {

    private String listbook_last_subtitle;
...

    @Override
       public void onCreate(Bundle savedInstanceState) {

        String thisFragSubtitle = (String) getActivity().getActionBar().getSubtitle();
        listbook_last_subtitle = thisFragSubtitle;
       }
...

    @Override
        public void onResume(){
            super.onResume();
            getActivity().getActionBar().setSubtitle(listbook_last_subtitle);
        }
...
 0
Author: Roshan Poudyal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-14 04:55:29
public abstract class RootFragment extends Fragment implements OnBackPressListener {

 @Override
 public boolean onBackPressed() {
  return new BackPressImpl(this).onBackPressed();
 }

 public abstract void OnRefreshUI();

}


public class BackPressImpl implements OnBackPressListener {

 private Fragment parentFragment;

 public BackPressImpl(Fragment parentFragment) {
  this.parentFragment = parentFragment;
 }

 @Override
 public boolean onBackPressed() {
  ((RootFragment) parentFragment).OnRefreshUI();
 }
}

Y la extensión final de su Framen de RootFragment para ver el efecto

 0
Author: Luu Quoc Thang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-24 08:39:54

Mi solución es obtener el título actual de la barra de acción en el Fragmento antes de configurarlo con el nuevo título. De esta manera, una vez que el Fragmento se revienta, puedo volver a ese título.

@Override
public void onResume() {
    super.onResume();
    // Get/Backup current title
    mTitle = ((ActionBarActivity) getActivity()).getSupportActionBar()
            .getTitle();
    // Set new title
    ((ActionBarActivity) getActivity()).getSupportActionBar()
        .setTitle(R.string.this_fragment_title);
}

@Override
public void onDestroy() {
    // Set title back
    ((ActionBarActivity) getActivity()).getSupportActionBar()
        .setTitle(mTitle);

    super.onDestroy();
}
 0
Author: Mahendran Candy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-19 07:41:03

He usado enum FragmentTags para definir todas mis clases de fragmentos.

TAG_FOR_FRAGMENT_A(A.class),
TAG_FOR_FRAGMENT_B(B.class),
TAG_FOR_FRAGMENT_C(C.class)

Pasa FragmentTags.TAG_FOR_FRAGMENT_A.name() como etiqueta de fragmento.

Y ahora en

@Override
public void onBackPressed(){
   FragmentManager fragmentManager = getFragmentManager();
   Fragment current
   = fragmentManager.findFragmentById(R.id.fragment_container);
    FragmentTags fragmentTag = FragmentTags.valueOf(current.getTag());

  switch(fragmentTag){
    case TAG_FOR_FRAGMENT_A:
        finish();
        break;
   case TAG_FOR_FRAGMENT_B:
        fragmentManager.popBackStack();
        break;
   case default: 
   break;
}
 0
Author: Ali,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-20 19:04:05