web-dev-qa-db-fra.com

Fragments onResume de la pile arrière

J'utilise le package de compatibilité pour utiliser Fragments avec Android 2.2. Lors de l’utilisation de fragments et de l’ajout de transitions entre eux, je voudrais obtenir le même comportement que l’onResume d’une activité, c’est-à-dire chaque fois qu’un fragment est amené au "premier plan" (visible pour l’utilisateur) après avoir été affiché. Dans le cas contraire, je souhaiterais qu'une sorte de rappel soit activé dans le fragment (pour effectuer certaines modifications sur une ressource d'interface utilisateur partagée, par exemple).

J'ai vu qu'il n'y a pas de callback intégré dans le cadre de fragment. Existe-t-il une bonne pratique pour y parvenir?

91
oriharel

Par manque d’une meilleure solution, j’ai eu ce travail pour moi: Supposons que j’ai une activité (MyActivity) et quelques fragments qui se remplacent (un seul est visible à la fois).

Dans MyActivity, ajoutez cet écouteur:

getSupportFragmentManager().addOnBackStackChangedListener(getListener());

(Comme vous pouvez le voir, j'utilise le package de compatibilité).

Implémentation 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() sera appelé après avoir appuyé sur "Retour". quelques mises en garde cependant:

  1. Il suppose que vous avez ajouté toutes les transactions Au backstack (en utilisant FragmentTransaction.addToBackStack())
  2. Il sera activé à chaque changement de pile (Vous pouvez stocker d'autres éléments dans La pile arrière, telle que l'animation), de manière à pouvoir obtenir plusieurs appels pour la même instance de fragment
108
oriharel

J'ai un peu changé la solution suggérée. Fonctionne mieux pour moi comme ça:

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
Brotoo25

Après une popStackBack(), vous pouvez utiliser le rappel suivant: onHiddenChanged(boolean hidden) dans votre fragment

5
user2230304

La section suivante de la rubrique Développeurs Android décrit un mécanisme de communication Création de rappels d’événements à l’activité . Pour en citer une ligne:

Un bon moyen de le faire est de définir une interface de rappel dans le fragment et de demander à l'activité de l'hôte de l'implémenter. Lorsque l'activité reçoit un rappel via l'interface, elle peut partager les informations avec d'autres fragments de la présentation si nécessaire.

Edit: Le fragment a une onStart(...) qui est appelée lorsque le fragment est visible par l'utilisateur. De même une onResume(...) quand visible et en cours d'exécution. Ceux-ci sont liés à leurs homologues d'activité . En bref: utilisez onResume()

3
PJL

Un peu amélioré et intégré à une solution de gestion.

Choses à garder à l'esprit. FragmentManager n'est pas un singleton, il ne gère que les fragments dans l'activité, il sera donc nouveau pour chaque activité. En outre, jusqu'à présent, cette solution ne prend pas en compte ViewPager, qui appelle la méthode setUserVisibleHint () pour contrôler la visibilité des fragments.

N'hésitez pas à utiliser les classes suivantes pour traiter ce problème (utilise l'injection Dagger2). Appel en activité:

//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
AAverin

Si un fragment est placé sur le backstack, Android détruit simplement sa vue. L'instance de fragment elle-même n'est pas tuée. Un moyen simple de commencer devrait écouter l’événement onViewCreated, une logique de mise "onResume ()" ici.

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
    }
1
Franjo

Dans mon activité onCreate ()

getSupportFragmentManager().addOnBackStackChangedListener(getListener());

Utilisez cette méthode pour attraper un fragment spécifique et appelez 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
Quan Nguyen

Ma solution consiste à obtenir le titre actuel de la barre d’action dans le fragment avant de le définir sur le nouveau titre. De cette façon, une fois que le fragment est sorti, je peux revenir à ce titre.

@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
Mahendran Candy

onResume () pour le fragment fonctionne 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
Roshan Poudyal

C'est la bonne réponse que vous pouvez appeler onResume () à condition que le fragment soit attaché à l'activité. Sinon, vous pouvez utiliser onAttach et onDetach

0
iamlukeyb
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();
 }
}

et dernier prolonger votre fragment de fragment de racine pour voir l'effet

0
Luu Quoc Thang

J'ai utilisé enum FragmentTags pour définir toutes mes classes de fragments.

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

passer FragmentTags.TAG_FOR_FRAGMENT_A.name() en tant que balise fragment.

et maintenant 

@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
Ali