Flex 4 (6) – Composants graphiques qui n'apparaissent plus après une migration depuis Flex 3 [Résolu]
J'ai eu aujourd'hui, un commentaire sur la page de la ToasterLib, me demandant s'il y avait une version Flex 4 de cette librairie. Je me suis donc attelé à la tâche cette après-midi. Même si mon apprentissage de Flex 4 est loin d'être au point, la ToasterLib n'a que peu de dépendances vers Flex (Canvas, Label tout au plus), je pensais donc que cela allait être aisé.
Mais je commence à comprendre pourquoi Adobe conseille de ne pas faire de migrations "bêtes et méchantes" Flex 3 vers Flex 4. Il faut en effet être méticuleux dans le remplacement des composants, connaître leurs équivalences et les petites modifications de comportement.
Migration de la ToasterLib
Pour créer une version utilisable avec Flex 4 de la ToasterLib, j'ai pris mon projet Flex 3 sous Flex Builder 3, j'ai crée une branche SVN nommée "ToasterLibFx4" et j'ai fait un checkout de ce projet dans Flash Builder 4. A l'import, je précise bien que je veux utiliser le SDK Flex 4.0 me voilà opérationnel.
Je commence donc à modifier les Canvas en SkinnableContainer, à mettre les paddings/horizontalCenter/verticalCenter sur les propriétés "layout" des composants (on verra cette partie plus tard dans les tutoriaux Flex 4). Après ces quelques modifications, mon projet d'exemples compile et fonctionne presque correctement en seulement 30 minutes! "Presque" correctement car déjà un premier problème se présente…
L'effet Move d'entrée est joué mais le composant n'est pas visible avant la fin de l'effet
Le premier effet qui est appliqué aux Toast est un effet d'arrivée crée par un Move appliqué au Toast. Petite surprise, celui-ci semble ne pas se produire et on voit seulement le Toast à la fin de la durée de l'effet (500ms). Avant ce délai, le composant n'est même pas visible. Les effets suivants (Blur, Fade) et même le Move qui se produit à la disparition du Toast sont par contre joués comme ils le devraient.
Première piste, vérifier que l'effet est bien joué pendant une durée correcte:
...
var moveEffect:Effect = _effectDescriptor.getMoveToStackTopEffect(toastMessage, moveFromPoint, moveToPoint);
moveEffect.addEventListener(EffectEvent.EFFECT_START, function onEffectStart(event:EffectEvent):void{trace ("start: " + getTimer());});
moveEffect.addEventListener(EffectEvent.EFFECT_END,function onEffectStart(event:EffectEvent):void{trace ("end: " + getTimer());});
moveEffect.play()
...
start: 3103
end: 3599
Pas de problème à ce niveau là, le Move est bien lancé quand on fait play(). Il faut donc chercher une autre piste.
Deuxième piste, vérifier que l'on ne lance pas l'effet trop tôt (de-sychronisation):
...
var moveEffect:Effect = _effectDescriptor.getMoveToStackTopEffect(toastMessage, moveFromPoint, moveToPoint);
setTimeout(moveEffect.play, 1000);
...
On décale ici la lecture de l'effet d'une seconde. A l'écran, on voit alors que le Toast apparait en (0,0) puis l'effet se joue, ce qui fait que le Toast "sursaute". Seulement, s'il on réduit le délai à 100ms, le composant ne s'affiche plus, comme avant. Avec l'habitude d'utiliser Flex, on acquiert un petit doigt spécial qui nous dit que le composant a besoin d'être entièrement crée (évènement FlexEvent.CREATION_COMPLETE) pour que l'effet puisse se jouer.
Voici donc le troisième test:
// TODO
// find a way to trigger the effect onCreationComplete without using an anonymous function
// that will prevent the toastmessage from being collected
toastMessage.addEventListener(FlexEvent.CREATION_COMPLETE, function onToastMessageCreationComplete(e:Event):void{moveEffect.play()});
Ici, on s'inscrit sur l'évènement CREATION_COMPLETE du toast. Quand celui-ci se produit, on lance l'effet. J'ai choisi ici de passer par une fonction anonyme qui me permet de garder directement la variable "moveEffect" dans le "scope" de la fonction. En effet, avec une méthode de handler classique, je n'aurai plus eu accès à la variable "moveEffect".
Attention, comme précisé dans le commentaire, ce n'est pas une bonne pratique car cela signifie que même fermés, les Toasts seront mal ou pas du tout détruits par le Garbage Collector. Dans le futur, il faudra trouver une meilleure solution
Presque sans surprise, cette solution fonctionne. Je ne sais pas pourquoi mais avec Flex 3, je n'avais pas besoin de faire cette manipulation. Peut-être est-ce un bug, je ne sais pas vraiment.
En passant d'une Box à un BorderContainer, les composants enfants ne s'affichent plus
Pour être plus cohérent avec Flex 4, je décide de "migrer" la classe ToastMessageBase. La classe ToastMessageBase est comme son nom l'indique, la classe dont on va hériter pour créer un "Toast". Cette classe, avec Flex 3, héritait de Box. Puisque j'ai besoin d'un "Canvas avec border", je choisis de remplacer Box par BorderContainer qui va me donner le même fonctionnel.
Flex Transition: Utilisation d'un Transition Effect Filter
Lorsque vous appliquez une transition, les effets sont appliqués à toutes les cibles en même temps. Parfois, ce comportement est le comportement souhaité. Cependant, parfois, vous voudrez que certains effets soient conditionnels, dépendant de quels changements se passent pour les cibles.
Par exemple, s'il on utilise cette transition:
<mx:transitions>
<mx:Transition fromState="*" toState="*">
<mx:Sequence targets="{[windowA, windowB, windowC, windowD]}">
<mx:Blur blurYFrom="0" blurYTo="10" duration="100"/>
<mx:Parallel >
<mx:Move/>
<mx:Resize/>
</mx:Parallel>
<mx:Blur blurYFrom="10" blurYTo="0" duration="100"/>
</mx:Sequence>
</mx:Transition>
</mx:transitions>
Flex Transition: Programmer des Transitions en ActionScript (Move, Resize…), avec exemple AS3
Les transitions fonctionnent de la même manière en MXML qu'un ActionScript (AS3) car vous devez fixer les mêmes propriétés et ces propriétés auront le même effet que vous utilisiez du MXML ou de l'ActionScript.
Vous pouvez construire une nouvelle instance de mx.states.Transition en utilisant le constructeur:
var transition:Transition = new Transition();
Vous pouvez ensuite fixer les propriétés fromState et toState:
transition.toState = "*"; transition.fromState = "*";
Vous pouvez maintenant assigner un effet en utilisant la propriété effect:
var move:Move = new Move(); move.targets = [textInput1, textInput2]; transition.effect = move;
Enfin, il vous suffira d'ajouter simplement cette transition à la propriété transitions de l'application ou du composant:
transitions = [transition];
Il n'y a pas de vrais avantage à utiliser une transition en AS3 plutôt qu'en MXML. Le choix que vous devrez faire sera basé sur le type de document sur lequel vous voulez définir les états. Si vous ajoutez des transitions à un document MXML, vous devriez utiliser le MXML pour définir ces transitions et inversement.
L'exemple suivant montre comment reproduire l'exemple de création de transitions en MXML mais cette fois, les transitions sont écrites en Action Script.
Flex Transition: Créer des Transitions en MXML (Move, Resize…), avec exemple
Toutes les applications et les composants ont une propriété states que vous pouvez utiliser pour définir les états à utiliser. De même, toutes les applications et les composants ont une propriété transitions qui est un tableau de toutes les transitions que vous voulez utiliser. Dans le MXML, vous pouvez définir la propriété transitions en utilisant directement dans le premier tag (root tag) d'une appli ou d'un composant:
<mx:transitions> <!-- les transitions entre états apparaitront ici--> </mx:transitions>
Tous les éléments du tableau de transitions, doivent être des object Transition. En MXML, vous créez des instances de Transition en utilisant le tag <mx:Transition>. Tous les objets Transition doivent définir une propriété fromState et toState, et ces propriétés doivent correspondre aux états entre lesquels la transition doit être appliquée.
Par exemple ce code va créer une transition depuis un état appelé A à un état appelé B:
<mx:Transition fromState="A" toState="B"/>
Si vous voulez utiliser une transition depuis ou vers un état en particulier, vous pouvez utiliser l'astérisque comme joker (wild card), ce qui veut dire "tous les états".
L'exemple suivant crée une transition entre tous les états et un état nommé B:
<mx:Transition fromState="*" toState="B"/>
Les objets Transitions doivent avoir une propriété effet qui détermine quel effet sera appliqué pendant le changement d'état. La propriété effet est la propriété par défaut lorsque vous créez un objet Transition en utilisant le MXML.
Flex Effect: Démarrer un effet avec un trigger (flex trigger event)
Un trigger se produit à l'intérieur d'une application Flex pour jouer un effet. L'utilisation des triggers permet de créer et d'appliquer des effets entièrement en MXML. Ce n'est pas nécéssairement mieux ou pire que d'utiliser la méthode play(). C'est juste une autre manière d'appliquer des effets.
Dans la terminologie Flex, un trigger combiné avec un effet est appelé un behavior (comportement)
Il existe des triggers standards, disponible pour tous les composants. La liste suivante présente ces triggers communs:
- addedEffect: Le composant a été ajouté à la liste d'affichage
- removedEffect: Le composant a été supprimé de la liste d'affichage
- creationCompleteEffect: Le composant a été crée et initialisé
- focusInEffect: Le composant a reçu le focus
- focusOutEffect: Le composant a perdu le focus
- hideEffect: Le composant a été caché (rendu non-visible)
- showEffect: Le composant a été affiché (rendu visible)
- rollOverEffect: L'utilisateur a passé sa souris sur le composant
- rollOutEffect: L'utilisateur à passé la souris hors du composant
- mouseDownEffect: L'utilisateur a préssé le bouton gauche de la souris au dessus du composant
- mouseUpEffect: L'utilisateur a relaché le bouton gauche de la souris au dessus du composant
- moveEffect: Les propriétés x et/ou y du composant ont changé
- resizeEffect: La hauteur et/ou la largeur du composant ont changé
Vous pouvez assigner l'instance d'un effet à un trigger pour un composant et cet effet sera appliqué automatiquement que le trigger se déclenchera. Lorsque vous utilisez les triggers, vous ne devez pas spécifier un target (cible) à cet effet. Au lieu de cela, le target est automatiquement défini lorsque l'effet est déclenché.





