J'ai écrit une activité factice qui bascule entre deux fragments. Lorsque vous passez de FragmentA à FragmentB, FragmentA est ajouté à la pile arrière. Cependant, lorsque je reviens à FragmentA (en appuyant sur le dos), un tout nouveau FragmentA est créé et l'état dans lequel il se trouvait est perdu. J'ai l'impression que je suis après la même chose que cette question, mais j'ai inclus un exemple de code complet pour aider à résoudre le problème:
public class FooActivity extends Activity {
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(android.R.id.content, new FragmentA());
transaction.commit();
}
public void nextFragment() {
final FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(android.R.id.content, new FragmentB());
transaction.addToBackStack(null);
transaction.commit();
}
public static class FragmentA extends Fragment {
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View main = inflater.inflate(R.layout.main, container, false);
main.findViewById(R.id.next_fragment_button).setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
((FooActivity) getActivity()).nextFragment();
}
});
return main;
}
@Override public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Save some state!
}
}
public static class FragmentB extends Fragment {
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.b, container, false);
}
}
}
Avec quelques messages de journal ajoutés:
07-05 14:28:59.722 D/OMG ( 1260): FooActivity.onCreate
07-05 14:28:59.742 D/OMG ( 1260): FragmentA.onCreateView
07-05 14:28:59.742 D/OMG ( 1260): FooActivity.onResume
<Tap Button on FragmentA>
07-05 14:29:12.842 D/OMG ( 1260): FooActivity.nextFragment
07-05 14:29:12.852 D/OMG ( 1260): FragmentB.onCreateView
<Tap 'Back'>
07-05 14:29:16.792 D/OMG ( 1260): FragmentA.onCreateView
Il n'appelle jamais FragmentA.onSaveInstanceState et crée un nouveau FragmentA lorsque vous répondez. Cependant, si je suis sur FragmentA et que je verrouille l'écran, FragmentA.onSaveInstanceState est appelé. Tellement bizarre ... ai-je tort de m'attendre à ce qu'un fragment ajouté à la pile arrière ne nécessite pas de recréation? Voici ce que disent les documents :
Alors que si vous appelez addToBackStack () lors de la suppression d'un fragment, le fragment est arrêté et sera repris si l'utilisateur revient.
ListView
. On dirait beaucoup trop de sauts de cercle pour attacher un écouteur de défilement et mettre à jour une variable d'instance.Réponses:
Si vous revenez à un fragment de la pile arrière, il ne recrée pas le fragment mais réutilise la même instance et commence par
onCreateView()
dans le cycle de vie du fragment, voir Cycle de vie du fragment .Donc, si vous souhaitez stocker un état, vous devez utiliser des variables d'instance et ne pas vous en remettre
onSaveInstanceState()
.la source
Comparé à Apple
UINavigationController
etUIViewController
, Google ne fonctionne pas bien dans l'architecture logicielle Android. Et le document d'Android surFragment
n'aide pas beaucoup.Lorsque vous entrez FragmentB à partir de FragmentA, l'instance FragmentA existante n'est pas détruite. Lorsque vous appuyez sur Retour dans FragmentB et que vous revenez à FragmentA, nous ne créons pas de nouvelle instance FragmentA. Les instances FragmentA existantes
onCreateView()
seront appelées.L'essentiel est que nous ne devrions pas gonfler à nouveau la vue dans FragmentA
onCreateView()
, car nous utilisons l'instance de FragmentA existante. Nous devons enregistrer et réutiliser le rootView.Le code suivant fonctionne bien. Il ne conserve pas seulement l'état des fragments, mais réduit également la charge de la RAM et du processeur (car nous ne gonflons que la mise en page si nécessaire). Je ne peux pas croire que l'exemple de code et de document de Google ne le mentionne jamais mais gonfle toujours la mise en page .
Version 1 (n'utilisez pas la version 1. Utilisez la version 2)
------ Mise à jour du 3 mai 2005: -------
Comme les commentaires l'ont mentionné,
_rootView.getParent()
est parfois nul dansonCreateView
, ce qui provoque le plantage. La version 2 supprime _rootView dans onDestroyView (), comme dell116 l'a suggéré. Testé sur Android 4.0.3, 4.4.4, 5.1.0.Version 2
AVERTISSEMENT!!!
C'est un HACK! Bien que je l'utilise dans mon application, vous devez tester et lire attentivement les commentaires.
la source
Je suppose qu'il existe une autre façon de réaliser ce que vous recherchez. Je ne dis pas que c'est une solution complète, mais cela a servi le but dans mon cas.
Ce que j'ai fait, c'est au lieu de remplacer le fragment, je viens d'ajouter le fragment cible. Donc, fondamentalement, vous utiliserez la
add()
méthode à la placereplace()
.Qu'est-ce que j'ai fait d'autre. Je cache mon fragment actuel et je l'ajoute également à backstack.
Par conséquent, il chevauche le nouveau fragment sur le fragment actuel sans détruire sa vue (vérifier que sa
onDestroyView()
méthode n'est pas appelée. De plus, l'ajouter àbackstate
me donne l'avantage de reprendre le fragment.Voici le code:
Le système AFAIK n'appelle que
onCreateView()
si la vue est détruite ou non créée. Mais ici, nous avons enregistré la vue en ne la supprimant pas de la mémoire. Cela ne créera donc pas une nouvelle vue.Et lorsque vous reviendrez du Fragment de destination, le dernier
FragmentTransaction
fragment supérieur supprimant apparaîtra, ce qui fera apparaître la vue la plus haute (SourceFragment) sur l'écran.COMMENTAIRE: Comme je l'ai dit, ce n'est pas une solution complète car elle ne supprime pas la vue du fragment Source et occupe donc plus de mémoire que d'habitude. Mais encore, servir le but. De plus, nous utilisons un mécanisme totalement différent de masquage de la vue au lieu de la remplacer, ce qui n'est pas traditionnel.
Ce n'est donc pas vraiment pour la façon dont vous maintenez l'état, mais pour la façon dont vous maintenez la vue.
la source
Activity A{Fragment A --> Fragment B}
lorsque je lance à nouveau l'application après avoir appuyé sur le bouton d'accueil, les deux fragmentsonResume()
sont appelés et par conséquent, ils commencent leur interrogation. Comment puis-je contrôler cela?Je suggérerais une solution très simple.
Prenez la variable de référence View et définissez la vue dans OnCreateView. Vérifiez si la vue existe déjà dans cette variable, puis renvoyez la même vue.
la source
if (_rootView.getParent() != null) { ((ViewGroup)_rootView.getParent()).removeView(_rootView); }
est approprié pour effacer la mémoire?null
unefragmentView
variable à laonDestroy()
méthode.onDestroyView()
. Cet effacement ne se produit pas pour notre variable de vue de sauvegarde (icifragmentView
) et provoquera une fuite de mémoire lorsque le fragment sera empilé / détruit. Vous pouvez trouver la même référence dans [Causes courantes des fuites de mémoire] ( square.github.io/leakcanary/fundamentals/… ) dans l'introduction de LeakCanery.Je suis tombé sur ce problème dans un fragment contenant une carte, qui a trop de détails de configuration pour enregistrer / recharger. Ma solution était de garder ce Fragment actif tout le temps (similaire à ce que @kaushal a mentionné).
Supposons que vous ayez le Fragment A actuel et que vous souhaitiez afficher le Fragment B.Résumé des conséquences:
Par conséquent, si vous souhaitez conserver les deux fragments "enregistrés", activez-les simplement en utilisant hide () / show ().
Avantages : méthode facile et simple pour garder plusieurs fragments en cours d'exécution
Inconvénients : vous utilisez beaucoup plus de mémoire pour tous les faire fonctionner. Peut rencontrer des problèmes, par exemple l'affichage de nombreuses grandes images bitmap
la source
onSaveInstanceState()
n'est appelé que s'il y a un changement de configuration.Depuis le passage d'un fragment à un autre, il n'y a pas de changement de configuration donc aucun appel à
onSaveInstanceState()
n'est là. Quel état n'est pas sauvé? Pouvez-vous préciser?Si vous entrez du texte dans EditText, il sera enregistré automatiquement. Tout élément d'interface utilisateur sans identifiant est l'élément dont l'état d'affichage ne doit pas être enregistré.
la source
onSaveInstanceState()
est également appelé lorsque le système détruit Activity car il manque de ressources.Ici, car
onSaveInstanceState
in fragment n'appelle pas lorsque vous ajoutez fragment dans backstack. Le cycle de vie du fragment dans la backstack lors de la restauration commenceonCreateView
et se termineonDestroyView
alors qu'ilonSaveInstanceState
est appelé entreonDestroyView
etonDestroy
. Ma solution est de créer une variable d'instance et d'initialiseronCreate
. Exemple de code:Et enregistrez-le
onActivityCreated
:la source
la source
Mon problème était similaire mais je me suis vaincu sans garder le fragment vivant. Supposons que vous ayez une activité qui a 2 fragments - F1 et F2. F1 est démarré initialement et disons qu'il contient des informations sur l'utilisateur, puis, à certaines conditions, F2 apparaît en demandant à l'utilisateur de remplir un attribut supplémentaire - son numéro de téléphone. Ensuite, vous voulez que ce numéro de téléphone revienne à F1 et termine l'inscription, mais vous réalisez que toutes les informations sur les utilisateurs précédents sont perdues et que vous ne disposez pas de leurs données précédentes. Le fragment est recréé à partir de zéro et même si vous avez enregistré ces informations dans
onSaveInstanceState
le bundle, il revient nul dansonActivityCreated
.Solution: enregistrez les informations requises en tant que variable d'instance dans l'activité d'appel. Passez ensuite cette variable d'instance dans votre fragment.
Donc, suite à mon exemple: avant d'afficher F2, j'enregistre les données utilisateur dans la variable d'instance à l'aide d'un rappel. Ensuite, je lance F2, l'utilisateur remplit le numéro de téléphone et appuie sur Enregistrer. J'utilise un autre rappel en activité, collecte ces informations et remplace mon fragment F1, cette fois il contient des données groupées que je peux utiliser.
Plus d'informations sur les callbacks peuvent être trouvées ici: https://developer.android.com/training/basics/fragments/communicating.html
la source
la source
Remplacez un fragment à l'aide du code suivant:
L'activité onBackPressed () est:
la source
la source
Solution parfaite qui trouve l'ancien fragment dans la pile et le charge s'il existe dans la pile.
utilisez-le comme
la source