web-dev-qa-db-fra.com

Fragments à l'intérieur des fragments

Je me demande s'il s'agit en fait d'un bogue dans l'API Android:

J'ai une configuration comme celle-ci:

┌----┬---------┐
|    |         |
|  1 |    2    |
|    |┌-------┐|
|    ||       ||
|    ||   3   ||
└----┴┴-------┴┘
  1. C’est un menu qui charge le fragment n ° 2 (Un écran de recherche) dans le volet de droite.
  2. Est un écran de recherche qui contient le fragment n ° 3, qui est une liste de résultats.
  3. La liste de résultats est utilisée à plusieurs endroits (y compris en tant que fragment de haut niveau fonctionnel).

Cette fonctionnalité fonctionne parfaitement sur un téléphone (où 1 & 2 et 3 sont ActivityFragments). 

Cependant, quand j'ai utilisé ce code:

    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();       
    Fragment frag = new FragmentNumber2();
    if(toLoad != null) frag.setArguments(toLoad);
    transaction.replace(R.id.rightPane, frag);      
    transaction.commit();

R.id.leftPane et R.id.rightPane sont <fragment>s dans une présentation linéaire horizontale.

Je crois comprendre que le code ci-dessus supprime le fragment qui est résident et le remplace ensuite par un nouveau fragment. Brillant ... Évidemment, ce n'est pas ce qui se produit car, lorsque ce code est exécuté à la deuxième fois, vous obtenez l'exception suivante:

07-27 15:22:55.940: ERROR/AndroidRuntime(8105): Caused by: Java.lang.IllegalArgumentException: Binary XML file line #57: Duplicate id 0x7f080024, tag null, or parent id 0x0 with another fragment for FragmentNumber3

Cela est dû au fait que le conteneur de FragmentNumber3 a été dupliqué et qu'il n'a plus d'identifiant unique. Le fragment initial n'a pas été détruit (?) Avant l'ajout du nouveau (dans mon esprit, cela signifie qu'il n'a pas été remplacé).

Quelqu'un peut-il me dire si cela est possible ( cette réponse suggère que ce n'est pas le cas) ou s'il s'agit d'un bug?

144
Graeme

Les fragments imbriqués ne sont actuellement pas pris en charge. Essayer de placer un fragment dans l'interface utilisateur d'un autre fragment entraînera un comportement indéfini et probablement cassé.

Mise à jour: Les fragments imbriqués sont pris en charge à partir d'Android 4.2 (et de la bibliothèque de support Android rev 11): http://developer.Android.com/about/versions/Android-4.2.html#NestedFragments

NOTE (selon this docs ): "Remarque: vous ne pouvez pas gonfler une mise en page dans un fragment lorsque cette mise en page inclut un <fragment>. Les fragments imbriqués ne sont pris en charge que s'ils sont ajoutés à un fragment de manière dynamique . "

202
hackbod

Les fragments imbriqués sont pris en charge dans Android 4.2 et versions ultérieures

Android Support Library prend également en charge les fragments imbriqués, ce qui vous permet d'implémenter des conceptions de fragments imbriqués sur Android 1.6 et versions ultérieures.

Pour imbriquer un fragment, appelez simplement getChildFragmentManager () sur le fragment dans lequel vous souhaitez ajouter un fragment. Cela renvoie un FragmentManager que vous pouvez utiliser comme vous le faites normalement à partir de l'activité de niveau supérieur pour créer des transactions de fragment. Par exemple, voici du code qui ajoute un fragment à partir d’une classe Fragment existante:

Fragment videoFragment = new VideoPlayerFragment();
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.add(R.id.video_fragment, videoFragment).commit();

Pour en savoir plus sur les fragments imbriqués, suivez ces didacticiels
Partie 1
Partie 2
Partie 3

et voici un SO post qui traite des meilleures pratiques pour les fragments imbriqués .

96
Raneez Ahmed

.. vous pouvez nettoyer votre fragment imbriqué dans la méthode destroyview du fragment parent:

@Override
    public void onDestroyView() {

      try{
        FragmentTransaction transaction = getSupportFragmentManager()
                .beginTransaction();

        transaction.remove(nestedFragment);

        transaction.commit();
      }catch(Exception e){
      }

        super.onDestroyView();
    }
33
furykid

J'ai une application que je suis en train de développer et qui est présentée de manière similaire avec des onglets dans la barre d'action qui lance des fragments. Certains de ces fragments contiennent de multiples fragments incorporés.

J'obtenais la même erreur lorsque j'ai essayé d'exécuter l'application. Il semble que si vous instanciez les fragments dans la présentation XML après avoir désélectionné un onglet puis resélectionné, j'obtiendrais l'erreur du gonfleur. 

J'ai résolu ce problème en remplaçant tous les fragments XML par Linearlayouts, puis en utilisant une transaction gestionnaire/fragment de fragment pour instancier les fragments, tout semble fonctionner correctement, au moins au niveau test actuellement.

J'espère que cela vous aide.

14
draksia

J'ai fait face au même problème, je me suis battu pendant deux jours et je devrais dire que le moyen le plus simple de surmonter ce problème est d'utiliser fragment.hide ()/fragment.show () lorsque l'onglet est sélectionné/désélectionné. () 

public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft)
{
    if (mFragment != null)
        ft.hide(mFragment);
}

Lorsque la rotation de l'écran se produit, tous les fragments parents et enfants sont correctement détruits. 

Cette approche présente également un avantage supplémentaire: l'utilisation de hide ()/show () ne provoque pas la perte de l'état des vues fragmentées. Il n'est donc pas nécessaire de restaurer la position de défilement précédente pour ScrollViews, par exemple.

Le problème est que je ne sais pas s’il est correct de ne pas détacher des fragments quand ils ne sont pas visibles. Je pense que l'exemple officiel de TabListener est conçu avec l'idée que les fragments sont réutilisables et que vous ne devriez pas polluer avec leur mémoire, cependant, je pense que si vous n'avez que quelques onglets et que vous savez que les utilisateurs basculeront fréquemment entre eux conviendra de les garder attachés à l’activité en cours.

J'aimerais entendre les commentaires de développeurs plus expérimentés. 

4
ievgen

Si vous constatez que votre fragment imbriqué n'est pas supprimé ou dupliqué (par exemple, au redémarrage de l'activité, à la rotation de l'écran), essayez de modifier:

transaction.add(R.id.placeholder, newFragment);

à

transaction.replace(R.id.placeholder, newFragment);

Si ci-dessus ne vous aide pas, essayez:

Fragment f = getChildFragmentManager().findFragmentById(R.id.placeholder);

FragmentTransaction transaction = getChildFragmentManager().beginTransaction();

if (f == null) {
    Log.d(TAG, "onCreateView: fragment doesn't exist");
    newFragment= new MyFragmentType();
    transaction.add(R.id.placeholder, newFragment);
} else {
    Log.d(TAG, "onCreateView: fragment already exists");
    transaction.replace(R.id.placeholder, f);
}
transaction.commit();

Appris ici

0
Voy