Flex 4 – (5) Utilisation de Group pour le rendu des IVisualElement
Dans les articles précédents de ce fil rouge Flex 4, on a vu comment différencier les Child des Element et comment est organisée la structure des composants Flex 4. On va maintenant parler des différences de rendu entre les Child et les Element. Notamment, lorsque l'on essaie d'utiliser un mix entre les composants Flex 3 et les composants Flex 4.
L'utilisation du composant Spark Group
Prenons un projet Flex 4 avec un conteneur Spark Panel (Flex 4), qui comprend plusieurs Child et plusieurs Element (Flex 4 donc):
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">
<s:Panel>
<s:layout>
<s:VerticalLayout />
</s:layout>
<s:Button label="Button 1" />
<s:Label text="Label 1" />
<s:Rect width="100" height="50">
<s:stroke>
<s:SolidColorStroke color="red" />
</s:stroke>
</s:Rect>
</s:Panel>
</s:Application>
Tout va bien, l'application compile correctement et nous montre bien notre Panel avec nos composants. Un petit aperçu:
Prenons maintenant un projet Flex 4 avec un conteneur MX Panel (Flex 3), qui comprend plusieurs Child et plusieurs Element:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">
<mx:Panel>
<s:Button label="Button 1" />
<s:Label text="Label 1" />
<s:Rect width="100" height="50">
<s:stroke>
<s:SolidColorStroke color="red" />
</s:stroke>
</s:Rect>
</mx:Panel>
</s:Application>
On sauvegarde, et là, le compilateur nous donne une erreur dans la vue Problems:
'Rect' is a graphic primitive that should be wrapped in a Group in order to be added to 'Panel'. ChildElements.mxml /ChildElements/src line 8 Flex Problem

On essaie de nous faire comprendre que pour utiliser Rect (ou une autre primitive) dans un composant Flex 3, il faut qu'il soit encapsulé (wrapped) dans un objet Group. Et effectivement, s'il on "wrap" notre composant Rect dans un Group, tout fonctionne:
<mx:Panel>
<s:Button label="Button 1" />
<s:Label text="Label 1" />
<s:Group>
<s:Rect width="100" height="50">
<s:stroke>
<s:SolidColorStroke color="red" />
</s:stroke>
</s:Rect>
</s:Group>
</mx:Panel>
Alors pourquoi cette nécessité d'avoir un Group autour de ses primitives? La réponse n'est pas simple, il faut commencer à bien comprendre comment fonctionne Flex 4 (vous les lisez mes articles, non ? ^^).
Comme on l'a vu, on peut ajouter soit des Child, soit des Element à un conteneur car les deux implémentent l'interface IVisualElement. Cependant, les composants "purement" Element, comme les primitives Spark implémentent une extension de IVisualElement qui est IGraphicElement. Cette interface IGraphicElement contient des méthodes supplémentaires à implémenter par rapport à IVisualElement dont le conteneur doit avoir connaissance.
Parmi ces méthodes, on trouve celles de partage de Display Object pour le rendu (DisplayObject sharing) avec entre autres les méthodes:
package spark.core{
public interface IGraphicElement extends IVisualElement{
function get displayObject():DisplayObject;
function get displayObjectSharingMode():String;
function set displayObjectSharingMode(value:String):void;
function createDisplayObject():DisplayObject;
function setSharedDisplayObject(sharedDisplayObject:DisplayObject):Boolean;
function canShareWithPrevious(element:IGraphicElement):Boolean;
function canShareWithNext(element:IGraphicElement):Boolean;
function parentChanged(parent:Group):void;
}
}
Ces fonctionnalités ne peuvent être gérées par les conteneurs MX. Comme Flex vous l'indique, il faut utiliser un Group, qui, lui est habilité à recevoir des primitives Spark.
Pour simplifier la chose, on ne peut, sur les conteneurs MX, que faire un "addChild". Quand on ajoute des éléments en MXML, Flex va faire des "addChild()" successifs:
Flex 3: public function addChild(child:DisplayObject):DisplayObject
Si vous essayez de faire un addElement() sur un conteneur MX, celui-ci va vous renvoyer une erreur à l'exécution. Pour être concret, voici le code de la méthode addElement() sur Container.as:
public function addElement(element:IVisualElement):IVisualElement
{
if (! (element is DisplayObject) )
throw ArgumentError(element + " is not supported in this Container");
return addChild(element as DisplayObject) as IVisualElement;
}
Vous le voyez, la méthode est là pour la compatibilité mais elle ne laissera passer un Element que si c'est Element est aussi un DisplayObject (dans ce cas-là, elle fait un addChild). Si on a un Element qui n'est pas un DisplayObject comme une primitive Spark, on aura une ArgumentError.
Avec les composants Flex 4, de base, Flex va faire de vrais addElement(). Les Element et les Child peuvent donc être ajoutés car ils sont gérés par les conteneurs Spark.
Partage de rendu entre les Element
J'en ai parlé plusieurs fois, les Element "purs" tels que les primitives Spark ont un rendu différent de celui des autres composants pour améliorer les performances des composants Flex 4. La principale amélioration est le rendu par des DisplayObject partagés.
Pour pouvoir être rendu, une primitive Spark a besoin d'un DisplayObject pour se dessiner. En effet, une primitive Spark n'est en fait qu'une définition d'un dessin. Typiquement, il n'a pas de propriété "graphics" sur lequel on peut faire nos moveTo() / lineTo() de dessin. Pour se dessiner, il va donc nous falloir un autre objet, ici, un DisplayObject.
En fait, pour chaque primitive Spark de la liste d'affichage, Flex ne va pas créer un DisplayObject unique. Il va créer des DisplayObject "quand il en a besoin". Vous ne maîtrisez pas ce processus, c'est un processus interne à Flash Player qui va s'en charger. Ainsi, pour dessiner 4 primitives Spark, Flex va peut-être décider qu'il n'a besoin que de 2 DisplayObject pour se dessiner. Ainsi, on aura eu moins d'instanciation d'objets (new DisplayObject()) et donc de meilleures performances.
Voici un exemple pour vous montrer que l'on aura pas 4 Child crées à la volée s'il on a 4 primitives à dessiner:
Flex Source Code Download: Télécharger le code source complet de l'application
Exemple inspiré de l'article de Bill White: Elements Vs Children
Essayez par vous même. Au départ, on a 4 Rect, qui sont 4 Element. Pourtant, on a seulement 2 Children (objets de type InvalidatingSprite). Ces 2 Children sont les DisplayObject crées automatiquement par Flex pour assurer le rendu des 4 Rect. Dans cet exemple, vous pouvez aussi ajouter des éléments (Rect). Ceux-ci vont être ajoutés à la suite de la liste des Element. Pourtant, cela ne va pas forcement créer un nouveau Children. Vous pourrez même remarquer que Flex va créer un nouveau Child de rendu quand on crée un Rect avec une rotation (la rotation du Rect est aléatoire).
Résumons un peu
La notion d'Element n'a pas été introduite par hasard dans Flex 4. C'est en fait le seul moyen pour pouvoir introduire les FXG. Pour rappel, FXG est le format d'échange graphique entre des logiciels tels que Adobe Illustrator ou Adobe Photoshop et Flash Catalyst.
Les FXG ne sont pas des DisplayObject et ne peuvent donc pas être ajouté comme Children (via addChild() car la méthode addChild prend un DisplayObject et cette méthode est une méthode de l'API Flash Player, impossible donc de la modifier). Pour contourner ce problème, Adobe a décidé d'introduire les Element pour les FXG et les DisplayObject. Ils ont caché les anciennes méthodes et ont ajouté de nouvelles pour les Element.
Articles similaires
- Flex 4 – (3) Différence entre éléments graphiques et éléments visuels (IVisualElement et DisplayObject)
- Flex 4 – Création d’une Skin de bouton Flex 4
- Flex 4 – (4) Structure des composants Flex 4 et comparaison avec Flex 3
- Flex 4 – Différences entre Flex 3 et Flex 4 (3-Nouveaux composants et container)
- Flex 4 – Différences entre Flex 3 et Flex 4 (5-Layout et Effects)
Aucun trackbacks pour l'instant







14 juin 2010
"Vous ne maîtrisez pas ce processus, c'est un processus interne à Flash Player qui va s'en charger."
-> Je pense que ce n'est pas le FP qui s'en charge directement, c'est plutôt un algo qui doit se trouver au niveau de la classe Group. A confirmer cependant
14 juin 2010
Oui tu as raison Florian, le partage d'objet se trouve sur Group mais on va dire que c'est tellement au fin fond de Flex et on ne peut tellement pas y accéder que c'est comme si ^^
Fabien