TL; DR: Je recherche un exemple de travail complet de ce que j'appellerai le scénario "l'animation à trois fragments de Gmail". Plus précisément, nous voulons commencer par deux fragments, comme ceci:
Lors d'un événement d'interface utilisateur (par exemple, en tapant sur quelque chose dans le fragment B), nous voulons:
- Fragment A pour glisser hors de l'écran vers la gauche
- Fragment B pour glisser vers le bord gauche de l'écran et rétrécir pour reprendre la place libérée par le fragment A
- Fragment C à glisser depuis le côté droit de l'écran et à occuper la place libérée par le Fragment B
Et, en appuyant sur le bouton BACK, nous voulons que cet ensemble d'opérations soit inversé.
Maintenant, j'ai vu beaucoup d'implémentations partielles; J'en passerai en revue quatre ci-dessous. En plus d'être incomplets, ils ont tous leurs problèmes.
@Reto Meier a contribué cette réponse populaire à la même question de base, indiquant que vous utiliseriez setCustomAnimations()
avec un FragmentTransaction
. Pour un scénario à deux fragments (par exemple, vous ne voyez que le fragment A au départ et que vous voulez le remplacer par un nouveau fragment B utilisant des effets animés), je suis entièrement d'accord. Toutefois:
- Étant donné que vous ne pouvez spécifier qu'une seule animation "d'entrée" et une "sortie", je ne vois pas comment vous géreriez toutes les différentes animations requises pour le scénario à trois fragments
- Le
<objectAnimator>
dans son exemple de code utilise des positions câblées en pixels, ce qui semblerait peu pratique étant donné les différentes tailles d'écran, maissetCustomAnimations()
nécessite des ressources d'animation, excluant la possibilité de définir ces choses en Java - Je ne sais pas comment les animateurs d'objets pour l'échelle sont liés à des choses comme
android:layout_weight
dans uneLinearLayout
allocation d'espace en pourcentage - Je suis à une perte sur la façon dont le fragment C est traitée au départ (
GONE
?android:layout_weight
De0
? Pré-animation à une échelle de 0? Autre chose?)
@Roman Nurik souligne que vous pouvez animer n'importe quelle propriété , y compris celles que vous définissez vous-même. Cela peut aider à résoudre le problème des positions câblées, au prix de l'invention de votre propre sous-classe de gestionnaire de mise en page personnalisée. Cela aide certains, mais je suis toujours déconcerté par le reste de la solution de Reto.
L'auteur de cette entrée pastebin montre un pseudo-code alléchant, disant essentiellement que les trois fragments résideraient initialement dans le conteneur, le fragment C étant caché au départ via une hide()
opération de transaction. Nous avons ensuite show()
C et hide()
A lorsque l'événement UI se produit. Cependant, je ne vois pas comment cela gère le fait que B change de taille. Cela repose également sur le fait que vous pouvez apparemment ajouter plusieurs fragments au même conteneur, et je ne suis pas sûr que ce comportement soit fiable ou non sur le long terme (pour ne pas mentionner que cela devrait casser findFragmentById()
, même si je peux vivre avec cela).
L'auteur de ce billet de blog indique que Gmail n'utilise pas setCustomAnimations()
du tout, mais utilise directement des animateurs d'objets ("il suffit de changer la marge gauche de la vue racine + de changer la largeur de la vue droite"). Cependant, il s'agit toujours d'une solution AFAICT à deux fragments, et la mise en œuvre a montré une fois de plus les dimensions des fils durs en pixels.
Je vais continuer à me débrouiller à ce sujet, donc je finirai peut-être par y répondre moi-même un jour, mais j'espère vraiment que quelqu'un a élaboré la solution à trois fragments pour ce scénario d'animation et pourra publier le code (ou un lien vers celui-ci). Les animations sous Android me donnent envie de m'arracher les cheveux, et ceux d'entre vous qui m'ont vu savent que c'est une entreprise largement infructueuse.
la source
Réponses:
J'ai téléchargé ma proposition sur github (fonctionne avec toutes les versions d'Android bien que l'accélération matérielle de la vue soit fortement recommandée pour ce type d'animations. Pour les appareils non accélérés par le matériel, une mise en cache bitmap devrait mieux convenir)
La vidéo de démonstration avec l'animation est arrivée (la fréquence d'images lente est la cause de la diffusion d'écran. Les performances réelles sont très rapides)
Usage:
Explication :
Création d'un nouveau RelativeLayout personnalisé (ThreeLayout) et de 2 animations personnalisées (MyScalAnimation, MyTranslateAnimation)
ThreeLayout
obtient le poids du volet gauche comme paramètre, en supposant que l'autre vue visible aweight=1
.Crée donc
new ThreeLayout(context,3)
une nouvelle vue avec 3 enfants et le volet gauche avec 1/3 de l'écran total. L'autre vue occupe tout l'espace disponible.Les animations de mise à l'échelle et de traduction redimensionnent et déplacent la vue et non pseudo- [redimensionner, déplacer]. Notez que ce
fillAfter(true)
n'est utilisé nulle part.View2 est à droite de View1
et
View3 est à droite_of View2
Après avoir défini ces règles, RelativeLayout s'occupe de tout le reste. Les animations modifient
margins
(en mouvement) et[width,height]
à l'échellePour accéder à chaque enfant (afin que vous puissiez le gonfler avec votre Fragment, vous pouvez appeler
Ci-dessous sont présentés les 2 animations
Étape 1
Étape 2
la source
View
plus petite), mais dans Gmail / Email, nous n'obtenons pas de polices plus petites, juste moins de mots.scaleX
valeurView
, bien que je puisse mal interpréter les choses.OK, voici ma propre solution, dérivée de l'application Email AOSP, selon la suggestion de @ Christopher dans les commentaires de la question.
https://github.com/commonsguy/cw-omnibus/tree/master/Animation/ThreePane
La solution de @ lowwire rappelle la mienne, bien qu'il utilise des classiques
Animation
plutôt que des animateurs, et qu'il utilise desRelativeLayout
règles pour appliquer le positionnement. Du point de vue de la prime, il obtiendra probablement la prime, à moins que quelqu'un d'autre avec une solution plus astucieuse ne publie encore une réponse.En un mot, le
ThreePaneLayout
dans ce projet est uneLinearLayout
sous - classe, conçue pour travailler en paysage avec trois enfants. Les largeurs de ces enfants peuvent être définies dans le format XML de mise en page, par tous les moyens souhaités - je montre en utilisant des poids, mais vous pouvez avoir des largeurs spécifiques définies par des ressources de dimension ou autre. Le troisième enfant - Fragment C dans la question - doit avoir une largeur de zéro.Cependant, au moment de l'exécution, quelle que soit la configuration initiale des largeurs, la gestion de la largeur est prise en charge
ThreePaneLayout
la première fois que vous utilisezhideLeft()
pour passer de l'affichage de ce que la question appelle les fragments A et B aux fragments B et C. Dans la terminologie deThreePaneLayout
- qui n'a pas de lien spécifique à des fragments - les trois pièces sontleft
,middle
etright
. Au moment où vous appelezhideLeft()
, nous enregistrons les taillesleft
etmiddle
et nous mettons à zéro tous les poids qui ont été utilisés sur l'un des trois, afin que nous puissions contrôler complètement les tailles. Au moment dehideLeft()
, nous définissons la taille deright
comme étant la taille d'origine demiddle
.Les animations sont doubles:
ViewPropertyAnimator
pour effectuer une translation des trois widgets vers la gauche de la largeur deleft
, en utilisant une couche matérielleObjectAnimator
sur une pseudo-propriété personnalisée demiddleWidth
pour changer lamiddle
largeur de ce avec quoi il a commencé à la largeur d'origine deleft
(il est possible que ce soit une meilleure idée d'utiliser un
AnimatorSet
etObjectAnimators
pour tous ceux-ci, bien que cela fonctionne pour le moment)(il est également possible que le
middleWidth
ObjectAnimator
nie la valeur de la couche matérielle, car cela nécessite une invalidation assez continue)(il est certainement possible que j'aie encore des lacunes dans ma compréhension d'animation et que j'aime les déclarations entre parenthèses)
L'effet net est que
left
glisse hors de l'écran,middle
glisse à la position et à la taille d'origineleft
et seright
traduit juste derrièremiddle
.showLeft()
inverse simplement le processus, avec le même mélange d'animateurs, juste avec les directions inversées.L'activité utilise a
ThreePaneLayout
pour contenir une paire deListFragment
widgets et aButton
. Sélectionner quelque chose dans le fragment de gauche ajoute (ou met à jour le contenu) du fragment du milieu. Sélectionner quelque chose dans le fragment du milieu définit la légende duButton
, plus s'exécutehideLeft()
sur leThreePaneLayout
. Appuyer sur BACK, si nous avons caché le côté gauche, exécuterashowLeft()
; sinon, BACK quitte l'activité. Puisque cela ne sert pasFragmentTransactions
à affecter les animations, nous sommes bloqués à gérer nous-mêmes cette «pile arrière».Le projet lié ci-dessus utilise des fragments natifs et le framework d'animation natif. J'ai une autre version du même projet qui utilise le backport des fragments de support Android et NineOldAndroids pour l'animation:
https://github.com/commonsguy/cw-omnibus/tree/master/Animation/ThreePaneBC
Le backport fonctionne bien sur un Kindle Fire de 1ère génération, bien que l'animation soit un peu saccadée étant donné les spécifications matérielles inférieures et le manque de prise en charge de l'accélération matérielle. Les deux implémentations semblent fluides sur un Nexus 7 et d'autres tablettes de la génération actuelle.
Je suis certainement ouvert aux idées sur la façon d'améliorer cette solution, ou d'autres solutions qui offrent des avantages évidents par rapport à ce que j'ai fait ici (ou à ce que @weakwire a utilisé).
Merci encore à tous ceux qui ont contribué!
la source
ListView
bouton du milieu et appuyé rapidement sur le bouton arrière et répété, toute la mise en page a dérivé vers la droite. Il a commencé à afficher une colonne d'espace supplémentaire à gauche. Ce comportement a été introduit parce que je ne laisserais pas la première animation (commencée par un appui sur le milieuListView
) se terminer et j'ai appuyé sur le bouton de retour. Je l'ai corrigé en remplaçanttranslationXBy
partranslationX
intranslateWidgets
method ettranslateWidgets(leftWidth, left, middle, right);
partranslateWidgets(0, left, middle, right);
inshowLeft()
method.requestLayout();
parmiddle.requestLayout();
dans lasetMiddleWidth(int value);
méthode. Cela peut ne pas affecter les performances car je suppose que le GPU fera toujours une mise en page complète, des commentaires?Nous avons construit une bibliothèque appelée PanesLibrary qui résout ce problème. C'est encore plus flexible que ce qui était proposé auparavant car:
Vous pouvez le vérifier ici: https://github.com/Mapsaurus/Android-PanesLibrary
Voici une démo: http://www.youtube.com/watch?v=UA-lAGVXoLU&feature=youtu.be
Il vous permet essentiellement d'ajouter facilement n'importe quel nombre de volets de taille dynamique et d'attacher des fragments à ces volets. J'espère que vous le trouverez utile! :)
la source
En vous basant sur l'un des exemples auxquels vous avez lié ( http://android.amberfog.com/?p=758 ), que diriez-vous d'animer la
layout_weight
propriété? De cette façon, vous pouvez animer le changement de poids des 3 fragments ensemble, ET vous obtenez le bonus qu'ils glissent tous bien ensemble:Commencez par une mise en page simple. Puisque nous allons animer
layout_weight
, nous avons besoin d'uneLinearLayout
vue racine pour les 3 panneaux:Puis le cours de démonstration:
De plus, cet exemple n'utilise pas FragmentTransactions pour réaliser les animations (il anime plutôt les vues auxquelles les fragments sont attachés), vous devrez donc faire toutes les transactions de backstack / fragment vous-même, mais par rapport à l'effort de faire fonctionner les animations gentiment, cela ne semble pas être un mauvais compromis :)
Horrible vidéo basse résolution en action: http://youtu.be/Zm517j3bFCo
la source
TextView
enwrap_content
s'étirant verticalement au fur et à mesure de l'animation). Cependant, il se peut que l'animation du poids soit la façon dont ils redimensionnent ce que j'ai appelé Fragment B. Merci!Cela n'utilise pas de fragments ... C'est une mise en page personnalisée avec 3 enfants. Lorsque vous cliquez sur un message, vous décalez les 3 enfants en utilisant
offsetLeftAndRight()
et un animateur.Dans
JellyBean
vous pouvez activer "Afficher les limites de mise en page" dans les paramètres "Options du développeur". Lorsque l'animation de la diapositive est terminée, vous pouvez toujours voir que le menu de gauche est toujours là, mais sous le panneau du milieu.C'est similaire au menu de l'application Fly-in de Cyril Mottier , mais avec 3 éléments au lieu de 2.
De plus, le
ViewPager
des troisièmes enfants est une autre indication de ce comportement:ViewPager
utilise généralement des fragments (je sais qu'ils ne sont pas obligés, mais je n'ai jamais vu d'implémentation autre queFragment
), et puisque vous ne pouvez pas utiliserFragments
dans un autreFragment
les 3 enfants ne sont probablement pas des fragments ...la source
TranslateAnimation
, même si je suis sûr qu'il existe des moyens d'utiliser des animateurs pour accomplir les mêmes fins. Je vais devoir faire quelques expériences là-dessus. Merci!J'essaie actuellement de faire quelque chose comme ça, sauf que le fragment B est mis à l'échelle pour prendre l'espace disponible et que le volet 3 peut être ouvert en même temps s'il y a assez de place. Voici ma solution jusqu'à présent, mais je ne suis pas sûr si je vais m'en tenir à elle. J'espère que quelqu'un fournira une réponse montrant la bonne voie.
Au lieu d'utiliser un LinearLayout et d'animer le poids, j'utilise un RelativeLayout et j'anime les marges. Je ne suis pas sûr que ce soit le meilleur moyen car il nécessite un appel à requestLayout () à chaque mise à jour. C'est fluide sur tous mes appareils.
Donc, j'anime la mise en page, je n'utilise pas de transaction de fragments. Je gère le bouton de retour manuellement pour fermer le fragment C s'il est ouvert.
FragmentB utilise layout_toLeftOf / ToRightOf pour le maintenir aligné sur les fragments A et C.
Lorsque mon application déclenche un événement pour afficher le fragment C, je glisse le fragment C et je glisse le fragment A en même temps. (2 animations séparées). Inversement, lorsque le Fragment A s'ouvre, je ferme C en même temps.
En mode portrait ou sur un écran plus petit, j'utilise une disposition légèrement différente et je fais glisser le fragment C sur l'écran.
Pour utiliser le pourcentage pour la largeur des fragments A et C, je pense que vous devriez le calculer au moment de l'exécution ... (?)
Voici la mise en page de l'activité:
L'animation pour faire glisser FragmentC vers l'intérieur ou l'extérieur:
la source