Voici le scénario: l'activité contient un fragment A
, qui à son tour utilise getChildFragmentManager()
pour ajouter des fragments A1
et A2
en onCreate
quelque sorte:
getChildFragmentManager()
.beginTransaction()
.replace(R.id.fragmentOneHolder, new FragmentA1())
.replace(R.id.fragmentTwoHolder, new FragmentA2())
.commit()
Jusqu'à présent, tout va bien, tout fonctionne comme prévu.
Nous exécutons ensuite la transaction suivante dans l'activité:
getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(anim1, anim2, anim1, anim2)
.replace(R.id.fragmentHolder, new FragmentB())
.addToBackStack(null)
.commit()
Pendant la transition, les enter
animations du fragment B
s'exécutent correctement mais les fragments A1 et A2 disparaissent entièrement . Lorsque nous annulons la transaction avec le bouton Retour, ils s'initialisent correctement et s'affichent normalement pendant l' popEnter
animation.
Lors de mes brefs tests, cela est devenu plus étrange - si j'ai défini les animations pour les fragments enfants (voir ci-dessous), l' exit
animation s'exécute par intermittence lorsque nous ajoutons un fragmentB
getChildFragmentManager()
.beginTransaction()
.setCustomAnimations(enter, exit)
.replace(R.id.fragmentOneHolder, new FragmentA1())
.replace(R.id.fragmentTwoHolder, new FragmentA2())
.commit()
L'effet que je veux obtenir est simple - je veux que l' animation exit
(ou devrait-elle être popExit
?) Sur le fragment A
(anim2) s'exécute, animant tout le conteneur, y compris ses enfants imbriqués.
Y a-t-il un moyen d'y parvenir?
Edit : Veuillez trouver un cas de test ici
Edit2 : Merci à @StevenByle de m'avoir poussé à continuer d'essayer avec les animations statiques. Apparemment, vous pouvez définir des animations par opération (et non globales pour l'ensemble de la transaction), ce qui signifie que les enfants peuvent avoir un ensemble d'animations statiques indéfini, tandis que leur parent peut avoir une animation différente et que le tout peut être validé en une seule transaction . Voir la discussion ci-dessous et le projet de cas de test mis à jour .
R.id.fragmentHolder
par rapport à A, A1, A2, etc.?changeFragment
méthode qu'une seule fois?Réponses:
Afin d'éviter que l'utilisateur ne voie les fragments imbriqués disparaître lorsque le fragment parent est supprimé / remplacé dans une transaction, vous pouvez "simuler" ces fragments toujours présents en fournissant une image d'eux, tels qu'ils apparaissent à l'écran. Cette image sera utilisée comme arrière-plan pour le conteneur de fragments imbriqués donc même si les vues du fragment imbriqué disparaissent, l'image simulera leur présence. De plus, je ne vois pas la perte de l'interactivité avec les vues du fragment imbriqué comme un problème car je ne pense pas que vous voudriez que l'utilisateur agisse sur eux alors qu'ils sont juste en train d'être supprimés (probablement comme une action de l'utilisateur comme bien).
J'ai fait un petit exemple avec la configuration de l'image d'arrière-plan (quelque chose de basique).
la source
Il semble donc y avoir beaucoup de solutions de contournement différentes pour cela, mais sur la base de la réponse de @ Jayd16, je pense avoir trouvé une solution fourre-tout assez solide qui permet toujours des animations de transition personnalisées sur des fragments enfants, et ne nécessite pas de faire un cache bitmap de la mise en page.
Ayez une
BaseFragment
classe qui s'étendFragment
et faites en sorte que tous vos fragments étendent cette classe (pas seulement les fragments enfants).Dans cette
BaseFragment
classe, ajoutez ce qui suit:Cela nécessite malheureusement une réflexion; cependant, puisque cette solution de contournement concerne la bibliothèque de prise en charge, vous ne courez pas le risque que l'implémentation sous-jacente change à moins que vous ne mettiez à jour votre bibliothèque de prise en charge. Si vous créez la bibliothèque de support à partir de la source, vous pouvez ajouter un accesseur pour l'ID de ressource d'animation suivant à
Fragment.java
et supprimer le besoin de réflexion.Cette solution supprime le besoin de "deviner" la durée de l'animation du parent (pour que l'animation "ne rien faire" ait la même durée que l'animation de sortie du parent), et vous permet de toujours faire des animations personnalisées sur des fragments enfants (par exemple si vous ' re échange de fragments enfants avec différentes animations).
la source
mNextAnim
est maintenant à l'intérieur d'unmAnimationInfo
objet. Vous pouvez y accéder comme ceci:Field animInfoField = Fragment.class.getDeclaredField("mAnimationInfo");
animInfoField.setAccessible(true);
Object animationInfo = animInfoField.get(fragment);
Field nextAnimField = animationInfo.getClass().getDeclaredField("mNextAnim");
val nextAnimResource = nextAnimField.getInt(animationInfo);
pour remplacer la ligneint nextAnimResource = nextAnimField.getInt(fragment);
J'ai pu trouver une solution assez propre. IMO c'est le moins hacky, et bien que ce soit techniquement la solution "dessiner un bitmap" au moins son abstraite par le fragment lib.
Assurez-vous que vos frags enfants remplacent une classe parent avec ceci:
Si nous avons une animation de sortie sur les frags enfants, ils seront animés au lieu de clignoter. Nous pouvons exploiter cela en ayant une animation qui dessine simplement les fragments enfants en alpha complet pendant une durée. De cette façon, ils resteront visibles dans le fragment parent pendant qu'il s'anime, donnant le comportement souhaité.
Le seul problème auquel je pense est de garder une trace de cette durée. Je pourrais peut-être le définir sur un nombre élevé, mais j'ai peur que cela puisse avoir des problèmes de performances s'il dessine toujours cette animation quelque part.
la source
Je poste ma solution pour plus de clarté. La solution est assez simple. Si vous essayez d'imiter l'animation de transaction de fragment du parent, ajoutez simplement une animation personnalisée à la transaction de fragment enfant avec la même durée. Oh et assurez-vous de définir l'animation personnalisée avant add ().
Le xml pour R.anim.none (la durée de l'animation d'entrée / sortie de mes parents est de 250 ms)
la source
fragmentTransaction.setCustomAnimations(R.anim.none, 0, R.anim.none, R.anim.none)
Je comprends que cela ne résoudra peut-être pas complètement votre problème, mais peut-être que cela conviendra aux besoins de quelqu'un d'autre, vous pouvez ajouter des animations
enter
/exit
etpopEnter
/popExit
à vos enfantsFragment
qui ne déplacent / n'animent pas réellement lesFragment
s. Tant que les animations ont la même durée / décalage que leursFragment
animations parentes , elles sembleront se déplacer / s'animer avec l'animation du parent.la source
vous pouvez le faire dans le fragment enfant.
la source
@@@@@@@@@@@@@@@@@@@@@@@@@@
EDIT: J'ai fini par désimplémenter cette solution car il y avait d'autres problèmes que cela posait. Square a récemment sorti 2 bibliothèques qui remplacent des fragments. Je dirais que cela pourrait en fait être une meilleure alternative que d'essayer de pirater des fragments pour faire quelque chose que Google ne veut pas qu'ils fassent.
http://corner.squareup.com/2014/01/mortar-and-flow.html
@@@@@@@@@@@@@@@@@@@@@@@@@@
J'ai pensé que je proposerais cette solution pour aider les personnes qui auront ce problème à l'avenir. Si vous retracez la conversation des affiches originales avec d'autres personnes et que vous regardez le code qu'il a publié, vous verrez que l'affiche originale arrive finalement à la conclusion de l'utilisation d'une animation sans opération sur les fragments enfants tout en animant le fragment parent. Cette solution n'est pas idéale car elle vous oblige à garder une trace de tous les fragments enfants, ce qui peut être fastidieux lors de l'utilisation d'un ViewPager avec FragmentPagerAdapter.
Depuis que j'utilise Child Fragments partout, j'ai proposé cette solution qui est efficace et modulaire (afin qu'elle puisse être facilement supprimée) au cas où ils le répareraient un jour et que cette animation sans opération ne serait plus nécessaire.
Il existe de nombreuses façons de mettre cela en œuvre. J'ai choisi d'utiliser un singleton, et je l'appelle ChildFragmentAnimationManager. Il gardera essentiellement une trace d'un fragment enfant pour moi en fonction de son parent et appliquera une animation sans opération aux enfants lorsqu'on le lui demandera.
Ensuite, vous devez avoir une classe qui étend Fragment que tous vos fragments étendent (au moins vos fragments enfants). J'avais déjà cette classe, et je l'appelle BaseFragment. Lorsqu'une vue de fragments est créée, nous l'ajoutons à ChildFragmentAnimationManager, et nous la supprimons lorsqu'elle est détruite. Vous pouvez le faire onAttach / Detach ou d'autres méthodes de correspondance dans la séquence. Ma logique pour choisir Créer / Détruire une vue était parce que si un fragment n'a pas de vue, je ne me soucie pas de l'animer pour continuer à être vu. Cette approche devrait également mieux fonctionner avec les ViewPagers qui utilisent des fragments, car vous ne garderez pas trace de chaque fragment qu'un FragmentPagerAdapter contient, mais plutôt de 3.
Maintenant que tous vos fragments sont stockés en mémoire par le fragment parent, vous pouvez appeler animate sur eux comme ceci, et vos fragments enfants ne disparaîtront pas.
Aussi, pour que vous l'ayez, voici le fichier no_anim.xml qui va dans votre dossier res / anim:
Encore une fois, je ne pense pas que cette solution soit parfaite, mais elle est bien meilleure que pour chaque instance, vous avez un fragment enfant, implémentant un code personnalisé dans le fragment parent pour suivre chaque enfant. J'y suis allé et ce n'est pas amusant.
la source
Je pense avoir trouvé une meilleure solution à ce problème que de créer un instantané du fragment actuel en une image bitmap, comme Luksprog l'a suggéré.
L'astuce est de se cacher le fragment en cours de suppression ou de détachement et ce n'est qu'une fois les animations terminées que le fragment est supprimé ou détaché dans sa propre transaction de fragment.
Imaginez que nous ayons
FragmentA
etFragmentB
, tous deux avec des sous-fragments. Maintenant, quand vous feriez normalement:Au lieu de cela tu fais
Passons maintenant à la mise en œuvre du fragment:
la source
J'avais le même problème avec le fragment de carte. Il a continué à disparaître pendant l'animation de sortie de son fragment contenant. La solution de contournement consiste à ajouter une animation pour le fragment de carte enfant qui le gardera visible pendant l'animation de sortie du fragment parent. L'animation du fragment enfant garde son alpha à 100% pendant sa durée.
Animation: res / animator / keep_child_fragment.xml
L'animation est ensuite appliquée lorsque le fragment de carte est ajouté au fragment parent.
Fragment parent
Enfin, la durée de l'animation du fragment enfant est définie dans un fichier de ressources.
values / integers.xml
la source
Pour animer la disparition des fragments imbriqués, nous pouvons forcer le pop back stack sur ChildFragmentManager. Cela déclenchera l'animation de transition. Pour ce faire, nous devons rattraper l'événement OnBackButtonPressed ou écouter les changements de backstack.
Voici un exemple avec du code.
la source
J'ai récemment rencontré ce problème dans ma question: les fragments imbriqués ne transitent pas correctement
J'ai une solution qui résout ce problème sans enregistrer une image bitmap, ni utiliser la réflexion ou toute autre méthode insatisfaisante.
Un exemple de projet peut être consulté ici: https://github.com/zafrani/NestedFragmentTransitions
Un GIF de l'effet peut être consulté ici: https://imgur.com/94AvrW4
Dans mon exemple, il y a 6 fragments enfants, répartis entre deux fragments parents. Je suis capable de réaliser les transitions pour entrer, sortir, pop et pousser sans aucun problème. Les modifications de configuration et les contre-pressions sont également gérées avec succès.
La majeure partie de la solution se trouve dans la fonction onCreateAnimator de mon BaseFragment (le fragment étendu par mes enfants et les fragments parents) qui ressemble à ceci:
L'activité et le fragment parent sont responsables de la définition des états de ces booléens. Il est plus facile de voir comment et où à partir de mon exemple de projet.
Je n'utilise pas de fragments de support dans mon exemple, mais la même logique peut être utilisée avec eux et leur fonction onCreateAnimation
la source
Un moyen simple de résoudre ce problème consiste à utiliser la
Fragment
classe de cette bibliothèque au lieu de la classe de fragment de bibliothèque standard:https://github.com/marksalpeter/contract-fragment
En remarque, le package contient également un modèle de délégué utile appelé
ContractFragment
que vous pourriez trouver utile pour créer vos applications en exploitant la relation de fragment parent-enfant.la source
D'après la réponse ci-dessus de @kcoppock,
si vous avez Activity-> Fragment-> Fragments (empilement multiple, ce qui suit aide), une modification mineure à la meilleure réponse IMHO.
la source
Mon problème concernait la suppression du fragment parent (ft.remove (fragment)), les animations enfants ne se produisaient pas.
Le problème de base est que les fragments enfants sont immédiatement DÉTRUITS AVANT que le fragment parents quitte l'animation.
Les animations personnalisées des fragments enfants ne sont pas exécutées lors de la suppression du fragment parent
Comme d'autres l'ont éludé, cacher le PARENT (et non l'enfant) avant le retrait du PARENT est la voie à suivre.
Si vous souhaitez réellement supprimer le parent, vous devez probablement configurer un écouteur sur votre animation personnalisée pour savoir quand l'animation est terminée, vous pouvez donc en toute sécurité effectuer une finalisation sur le fragment parent (supprimer). Si vous ne le faites pas, en temps opportun, vous pourriez finir par tuer l'animation. NB l'animation se fait sur sa propre file d'attente asynchrone.
BTW, vous n'avez pas besoin d'animations personnalisées sur le fragment enfant, car elles hériteront des animations parent.
la source
Le problème est résolu dans
androidx.fragment:fragment:1.2.0-alpha02
. Voir https://issuetracker.google.com/issues/116675313 pour plus de détails.la source
Vieux fil, mais au cas où quelqu'un trébucherait ici:
Toutes les approches ci-dessus me semblent très peu attrayantes, la solution bitmap est très sale et non performante; les autres exigent que les fragments enfants connaissent la durée de la transition utilisée dans la transaction utilisée pour créer le fragment enfant en question. Une meilleure solution à mes yeux, c'est quelque chose comme ce qui suit:
Nous masquons simplement le fragment actuel et ajoutons le nouveau fragment, lorsque l'animation est terminée, nous supprimons l'ancien fragment. De cette façon, il est géré en un seul endroit et aucun bitmap n'est créé.
la source