Apache Adobe Flex TutorialTutoriaux Adobe Flex & AIR en Français

27juin/103

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.

Une ou deux retouches avec les layouts et une fois que les propriétés sont bien en ordre et que le projet compile, je relance mon application exemple. A ce moment-là, le Toast apparait, mais uniquement le fond. Voici par exemple le code que j'avais pour le Toast avec la skin Avast (si vous trouvez l'erreur du premier coup, vous avez vraiment de bons yeux !):

<?xml version="1.0" encoding="utf-8"?>
<ToastMessageBase xmlns="com.fnicollet.toaster.message.*"
                  xmlns:s="library://ns.adobe.com/flex/spark"
                  xmlns:mx="http://www.adobe.com/2006/mxml"
                  width="211" height="155" borderColor="#EFEFEF"
                  borderStyle="solid" backgroundColor="#32B639" backgroundAlpha=".8"
                 >
  <layout>
    <s:VerticalLayout paddingTop="5" horizontalAlign="center"/>
  </layout>
  <mx:Script>
    <![CDATA[
      import mx.controls.Alert;
      [Bindable]
      public var sampleCaption:String = "A new avast! version is available online.\r\rClick here to install it...";

      private function onTextRollOver():void {
        message.setStyle("textDecoration", "underline");
      }

      private function onTextRollOut():void {
        message.setStyle("textDecoration", "none");
      }

      private function onClick():void {
        Alert.show("Click Update...");
        close();
      }
    ]]>
  </mx:Script>
  <s:Label id="title" fontWeight="bold" fontSize="16" x="80" y="15" text="avast! Information"
            fontFamily="Arial" color="white"/>
  <s:Line width="80%" >
    <s:stroke>
      <mx:SolidColorStroke color="white" weight="2" />
    </s:stroke>
  </s:Line>
  <mx:Text id="message" x="80" y="30" fontSize="13" fontFamily="Arial" text="{sampleCaption}"
           width="160" selectable="false" color="white" textAlign="center"
           rollOver="onTextRollOver()" rollOut="onTextRollOut()" useHandCursor="true"
           buttonMode="true" mouseChildren="false" click="onClick();"/>

</ToastMessageBase>

Alors, avez-vous trouvé ce qui cloche? Il m'a fallu de nombreux tests (voire certains un peu stupides) pour arriver à trouver d'où venait le problème.

Au bout d'une 30aine de minutes, je me suis aperçu que ce sont en fait le namespace MX qui n'est pas correct! Pour avoir une migration rapide, j'ai rajouté:

xmlns:s="library://ns.adobe.com/flex/spark"

Et j'ai laissé l'ancien namespace MX de Flex 3 pour mon bloc Script et mon composant Text:

xmlns:mx="http://www.adobe.com/2006/mxml"

Et bien sachez-le, on ne peut pas mixer les namespaces n'importe comment. Il faut utiliser le nouveau namespace MX pour être compatible Flex 4:

xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"

Mon composant Text reste avec un préfixe MX et mon bloc Script prend le préfixe FX.

Voilà, des erreurs de "débutant" que j'ai préféré partager afin que ceux d'entre vous qui tentent une conversion Flex 3 -> Flex 4 ne se fassent pas piéger :)

Articles similaires

Commentaires (3) Trackbacks (0)
  1. Faut vraiment que je passe à Flex 4, mais je n'arrive pas me décider … pourtant l'annonce d'un skin plus simple des composants me séduit au pus haut point

    Allez, motivé, je vais suivre tes derniers tutos

    Merci encore pour le partage de connaissances et bon dev

  2. Salut,

    Avant tout, merci pour tous les tutos, très intéressant...

    un petit truc pour enlever le listener dans une méthode anonyme:

    Actionscript:
    1. toastMessage.addEventListener(FlexEvent.CREATION_COMPLETE, function onToastMessageCreationComplete(e:Event):void
    2. {
    3. e.currentTarget.removeEventListener( e.type, arguments.callee );
    4. moveEffect.play()
    5. });

    JB

  3. Salut,
    Effectivement, j'ai découvert ça ya pas longtemps !
    Merci :)
    Fabien


Leave a comment

(required)

Aucun trackbacks pour l'instant