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

8mai/1147

AIR Mobile – Les itemRenderer de List (LabelItemRenderer, IconItemRenderer, …)

Dans un des billets précédents, on a vu quels étaient les nouveaux composants Flex 4.5:

AIR Mobile – Les composants et le thème Mobile Flex 4.5

Mais il y a aussi d'autres composants qui sont un peu à part qui sont les itemRenderer. Comme vous le savez peut-être déjà, ce que l'on appelle un "itemRenderer" dans le langage Flex est un composant qui va être utilisé au sein d'une liste. Pour une liste, on a des lignes de données. Pour chaque ligne de données, on va devoir effectuer le rendu d'un point de vue graphique de cette ligne. Le rendu le plus basique est celui d'afficher un texte, tout simple.

Mais vous voudrez parfois représenter des données plus complexes, avec plusieurs champs ou des composants graphiques différents comme des images par exemple. Vous allez alors créer votre propre composant qui va afficher les données de votre liste comme vous le souhaitez, c'est ce que l'on appelle un "itemRenderer".

Utiliser les itemRenderer de Flex 4.5?

Il est donc possible de créer vos propres itemRenderer ou d'utiliser ceux prévus dans le SDK. Alors pourquoi utiliser ceux du SDK?

Et bien les itemRenderer du SDK comme LabelItemRenderer et IconItemRenderer sont optimisés à l’extrême pour un affichage le plus rapide possible. Les performances que vous aurez avec un IconItemRenderer seront toujours meilleurs que si vous crééz le votre avec 2 labels et une image. Les optimisations sont faîtes notamment sur les points suivants:

  • Cache sur les images + délai d'affichage lors du scrolling
  • Gestion des DPI pour les espacements
  • Rendu des textes optimisé par l'utilisation du composant StyleableTextField
  • Gestion des invalidations

C'est donc toute cette logique que vous n'aurez pas à coder. Mais si votre itemRenderer ne correspond pas à un itemRenderer pré-mâché du SDK, vous avez toujours la possibilité de créer le votre. S'il n'y a que des modifications mineures, je vous conseille cependant d'utiliser l'héritage pour ajouter vos modifications. Si vous partez vraiment from scratch, n'hésitez pas à vous inspirer du code des classes de Flex qui contiennent les meilleurs techniques d'optimisation.

Utilisation du LabelItemRenderer

Voici l'itemRenderer le plus simple, il va vous permettre d'afficher un Label.

Un petit exemple d'utilisation:

<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
		xmlns:s="library://ns.adobe.com/flex/spark" title="HomeView">
	<fx:Declarations>
		<s:ArrayCollection id="ac">
			<fx:Array>
				<fx:String>Ligne 1</fx:String>
				<fx:String>Ligne 2</fx:String>
				<fx:String>Ligne 3</fx:String>
				<fx:String>Ligne 4</fx:String>
				<fx:String>Ligne 5</fx:String>
				<fx:String>Ligne 6</fx:String>
				<fx:String>Ligne 7</fx:String>
				<fx:String>Ligne 8</fx:String>
			</fx:Array>
		</s:ArrayCollection>
	</fx:Declarations>
	<s:List width="100%" height="100%" dataProvider="{ac}">
		<s:itemRenderer>
			<fx:Component>
				<s:LabelItemRenderer />
			</fx:Component>
		</s:itemRenderer>
	</s:List>
</s:View>

Et voilà le rendu dans le simulateur:
list
Pas impressionnant du tout mais les performances sont excellentes :) .

Utilisation du composant IconItemRenderer

IconItemRenderer est un composant qui hérite de LabelItemRenderer, vous permettant d'aller bien plus loin dans l'affichage et dans la personnalisation.

Voici les éléments qu'il peut contenir:

  • Un label "principal"
  • Un label "secondaire" appelé "message" qui peut s'étendre sur plusieurs lignes
  • Une image sur la gauche
  • Une image sur la droite appelée "decorator", un chevron par exemple

Ce composant permet de réaliser la plupart des itemRenderers assez "classiques" que l'on peut retrouver sur les interfaces natives. Bien sûr, vous pouvez modifier les styles de chaque élément pour obtenir le style que vous souhaitez.

Voici un petit exemple:

<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
		xmlns:s="library://ns.adobe.com/flex/spark" title="HomeView">
	<fx:Declarations>
		<s:ArrayCollection id="ac">
			<fx:Array>
				<fx:Object mainLabel="Label ligne 1" messageLabel="message ligne 1" />
        <fx:Object mainLabel="Label ligne 2" messageLabel="message ligne 2" />
        <fx:Object mainLabel="Label ligne 3" messageLabel="message ligne 3" />
        <fx:Object mainLabel="Label ligne 4" messageLabel="message ligne 4" />
        <fx:Object mainLabel="Label ligne 5" messageLabel="message ligne 5" />
        <fx:Object mainLabel="Label ligne 6" messageLabel="message ligne 6" />
        <fx:Object mainLabel="Label ligne 7" messageLabel="message ligne 7" />
        <fx:Object mainLabel="Label ligne 8" messageLabel="message ligne 8" />
			</fx:Array>
		</s:ArrayCollection>
	</fx:Declarations>
	<s:List width="100%" height="100%" dataProvider="{ac}">
		<s:itemRenderer>
			<fx:Component>
				<s:IconItemRenderer messageField="messageLabel" labelField="mainLabel" iconFunction="myIconFunction"
                            decorator="@Embed('chev.jpg')">
          <fx:Script>
            <![CDATA[
              [Embed(source='googleearth-icon.png')]
              private const ICON:Class;
              private function myIconFunction(item:Object):Class{
                return ICON;
              }
            ]]>
          </fx:Script>
        </s:IconItemRenderer>
			</fx:Component>
		</s:itemRenderer>
	</s:List>
</s:View>

Et voilà le rendu de cet IconItemRenderer:
list-2
Bien sûr, vous pouvez personnaliser l'icône et le decorator de chaque ligne.
Voilà un petit exemple réalisé simplement en modifiant quelques propriétés CSS sur de la donnée dynamique:

SC20110504-230405

Articles similaires

Commentaires (47) Trackbacks (0)
  1. J'ai essayé de modifier quelques propriétés CSS :

    @namespace s "library://ns.adobe.com/flex/spark";

    s|View
    {
    backgroundColor: #494949;
    chromeColor: #414141;
    contentBackgroundColor: #414141;
    alternatingItemColors: #414141;
    }
    global
    {
    color: white;
    }

    mais je n'arrive pas à modifier la couleur du séparateur d'items du composant List qui devient blanc, ce qui n'est pas très esthétique, je voudrais qu'il soit gris (#414141).
    Si quelqu'un sait comment faire, merci.

  2. Salut,
    on dirait que cela n'est pas prévu pour pouvoir être modifié par du CSS. Si tu regardes le code de LabelItemRenderer (ligne 873), tu as:
    // separators are a highlight on the top and shadow on the bottom
    topSeparatorColor = 0xFFFFFF;
    topSeparatorAlpha = .3;
    bottomSeparatorColor = 0×000000;
    bottomSeparatorAlpha = .3;

    Donc tu auras toujours une ligne noire et une ligne blanche. Ou alors, il faut qu'au lieu d'utiliser LabelItemRenderer, tu crée une classe qui hérite de LabelItemRenderer et qui redéfinit la méthode drawBackground.

    Fabien

  3. comment procéder pour créer une classe qui hérite de LabelItemRenderer et qui redéfinit la méthode drawBackground, merci.

  4. Dans le projet, click droit, New> ActionScript Class> Lui donner un nom. Ensuite, dans le corps de la classe:
    override protected function drawBackground(unscaledWidth:Number,
    unscaledHeight:Number):void {
    // … code
    }

    Mais avant de faire cela, je vous conseille quand même de monter en compétence en ActionScript (voir tutoriaux sur http://www.flex-tutorial.fr/tutorial-flex-liste/) pour apprendre les bases de la programmation orientée objet

    Fabien

  5. Cela devrait fonctionner, ne pas oublier d'utiliser cette classe et pas l'itemRenderer de base.

    Fabien

  6. la couleur ne change pas, la classe et le mxml

    Actionscript:
    1. package
    2. {
    3. import flash.display.GradientType;
    4. import flash.events.Event;
    5. import flash.geom.Matrix;
    6. import flash.text.TextLineMetrics;
    7.  
    8. import mx.controls.listClasses.*;
    9. import mx.core.DPIClassification;
    10. import mx.core.FlexGlobals;
    11. import mx.core.IDataRenderer;
    12. import mx.core.IFlexDisplayObject;
    13. import mx.core.ILayoutElement;
    14. import mx.core.UIComponent;
    15. import mx.core.mx_internal;
    16. import mx.events.FlexEvent;
    17.  
    18. import spark.components.supportClasses.InteractionState;
    19. import spark.components.supportClasses.InteractionStateDetector;
    20. import spark.components.supportClasses.StyleableTextField;
    21.  
    22. use namespace mx_internal;
    23.  
    24. import  spark.components.LabelItemRenderer;
    25.   public class DrawBackgroundIR extends LabelItemRenderer
    26. {
    27.  
    28.     override protected function drawBackground(unscaledWidth:Number,
    29.                                       unscaledHeight:Number):void
    30.     {
    31.         // figure out backgroundColor
    32.         var backgroundColor:*;
    33.         var downColor:* = getStyle("downColor");
    34.         var drawBackground:Boolean = true;
    35.        
    36.         if (down &amp;&amp; downColor !== undefined)
    37.         {
    38.             backgroundColor = downColor;
    39.         }
    40.         else if (selected)
    41.         {
    42.             backgroundColor = getStyle("selectionColor");
    43.         }
    44.         else if (hovered)
    45.         {
    46.             backgroundColor = getStyle("rollOverColor");
    47.         }
    48.         else if (showsCaret)
    49.         {
    50.             backgroundColor = getStyle("selectionColor");
    51.         }
    52.         else
    53.         {
    54.             var alternatingColors:Array;
    55.             var alternatingColorsStyle:Object = getStyle("alternatingItemColors");
    56.  
    57.             if (alternatingColorsStyle)
    58.                 alternatingColors = (alternatingColorsStyle is Array) ? (alternatingColorsStyle as Array) : [alternatingColorsStyle];
    59.            
    60.             if (alternatingColors &amp;&amp; alternatingColors.length&gt; 0)
    61.             {
    62.                 // translate these colors into uints
    63.                 styleManager.getColorNames(alternatingColors);
    64.                
    65.                 backgroundColor = alternatingColors[itemIndex % alternatingColors.length];
    66.             }
    67.             else
    68.             {
    69.                 // don't draw background if it is the contentBackgroundColor. The
    70.                 // list skin handles the background drawing for us.
    71.                 drawBackground = false;
    72.             }
    73.  
    74.         }
    75.        
    76.         // draw backgroundColor
    77.         // the reason why we draw it in the case of drawBackground == 0 is for
    78.         // mouse hit testing purposes
    79.         graphics.beginFill(backgroundColor, drawBackground ? 1 : 0);
    80.         graphics.lineStyle();
    81.         graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);
    82.         graphics.endFill();
    83.        
    84.         var topSeparatorColor:uint;
    85.         var topSeparatorAlpha:Number;
    86.         var bottomSeparatorColor:uint;
    87.         var bottomSeparatorAlpha:Number;
    88.        
    89.         // Selected and down states have a gradient overlay as well
    90.         // as different separators colors/alphas
    91.         if (selected || down)
    92.         {
    93.             var colors:Array = [0x000000, 0x000000 ];
    94.             var alphas:Array = [.2, .1];
    95.             var ratios:Array = [0, 255];
    96.             var matrix:Matrix = new Matrix();
    97.            
    98.             // gradient overlay
    99.             matrix.createGradientBox(unscaledWidth, unscaledHeight, Math.PI / 2, 0, 0 );
    100.             graphics.beginGradientFill(GradientType.LINEAR, colors, alphas, ratios, matrix);
    101.             graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);
    102.             graphics.endFill();
    103.         }
    104.  
    105.         // separators are a highlight on the top and shadow on the bottom
    106.         topSeparatorColor = 0x414141;
    107.         topSeparatorAlpha = 0.3;
    108.         bottomSeparatorColor = 0x000000;
    109.         bottomSeparatorAlpha = 0.3;
    110.        
    111.            
    112.         // draw separators
    113.         // don't draw top separator for down and selected states
    114.         if (!(selected || down))
    115.         {
    116.             graphics.beginFill(topSeparatorColor, topSeparatorAlpha);
    117.             graphics.drawRect(0, 0, unscaledWidth, 1);
    118.             graphics.endFill();
    119.         }
    120.        
    121.         graphics.beginFill(bottomSeparatorColor, bottomSeparatorAlpha);
    122.         graphics.drawRect(0, unscaledHeight - (isLastItem ? 0 : 1), unscaledWidth, 1);
    123.         graphics.endFill();
    124.        
    125.        
    126.         // add extra separators to the first and last items so that
    127.         // the list looks correct during the scrolling bounce/pull effect
    128.         // top
    129.         if (itemIndex == 0)
    130.         {
    131.             graphics.beginFill(bottomSeparatorColor, bottomSeparatorAlpha);
    132.             graphics.drawRect(0, -1, unscaledWidth, 1);
    133.             graphics.endFill();
    134.         }
    135.        
    136.         // bottom
    137.         if (isLastItem)
    138.         {
    139.             // we want to offset the bottom by 1 so that we don't get
    140.             // a double line at the bottom of the list if there's a
    141.             // border
    142.             graphics.beginFill(topSeparatorColor, topSeparatorAlpha);
    143.             graphics.drawRect(0, unscaledHeight + 1, unscaledWidth, 1);
    144.             graphics.endFill();
    145.         }
    146.        
    147.  
    148.     }
    149. }

    MXML:
    1. @namespace s "library://ns.adobe.com/flex/spark";
    2.  
    3. s|View
    4. {
    5.     backgroundColor: #494949;
    6.     chromeColor: #414141;
    7.     contentBackgroundColor: #414141;
    8.     alternatingItemColors: #414141;
    9. }
    10. global
    11. {
    12.     color: white;
    13. }
    14.  
    15.  
    16. <!-- Place non-visual elements (e.g., services, value objects) here -->

  7. @namespace s "library://ns.adobe.com/flex/spark";

    s|View
    {
    backgroundColor: #494949;
    chromeColor: #414141;
    contentBackgroundColor: #414141;
    alternatingItemColors: #414141;
    }
    global
    {
    color: white;
    }

  8. si je mets dans la balise List itemRenderer="DrawBackgroundIR" et que j'ai comme dans le tuto s:IconItemRenderer
    le compilateur me renvoie plusieurs initialisateurs pour la propriété 'itemRenderer'

  9. Il faut supprimer la balise iconitemrenderer si vous utilisez le votre

  10. Avec iconItemRenderer il n'y a pas moyen de modifier la couleur du séparateur d'item ?

  11. finalement ça fonctionne avec IconItemRenderer, j'ai créé un composant avec ma classe DrawBackgroundIR

    <s:View xmlns:components="components.*"

  12. Il y a quelque chose que je comprends pas
    quand je mets import spark.components.LabelItemRenderer dans ma classe DrawBackgroundIR
    je suis quand même obligé d'importer les classes déjà présentes dans spark.components.LabelItemRenderer

    import flash.display.GradientType;
    import flash.events.Event;
    import flash.geom.Matrix;
    import flash.text.TextLineMetrics;
     
    import mx.controls.listClasses.*;
    import mx.core.DPIClassification;
    import mx.core.FlexGlobals;
    import mx.core.IDataRenderer;
    import mx.core.IFlexDisplayObject;
    import mx.core.ILayoutElement;
    import mx.core.UIComponent;
    import mx.core.mx_internal;
    import mx.events.FlexEvent;
     
    import spark.components.supportClasses.InteractionState;
    import spark.components.supportClasses.InteractionStateDetector;
    import spark.components.supportClasses.StyleableTextField;

    sinon j'ai une erreur du compilateur

  13. Salut,
    Cool que cela fonctionne au final. Tu n'es sûrement pas obligé d'importer toutes les classes définies dans IconItemRenderer, juste celle que tu utilises dans DrawBackgroundIR. Pour faire un clean, click droit > Source > Organize Imports.

    Fabien

  14. Bonjour,

    "Voilà un petit exemple réalisé simplement en modifiant quelques propriétés CSS sur de la donnée dynamique"

    Possible d'avoir le code de l'exemple ? Merci !

  15. Salut,
    Désolé, ce n'est pas possible (bout de code propriétaire) mais ce n'est pas très dûr à effectuer. Juste une couleur de fond en utilisant la propriété CSS "alternatingItemColors", un IconItemRenderer avec un icone et un decorator :)

    Fabien

  16. En fait c'était plutôt l'aspect dynamique qui m'intriguais.

    Je définis une icone dans mon objet item
    [as][/as]
    et j'imagine que myIconFunction doit donc renvoyer qqch comme item.icon mais il va renvoyer la chaîne et non l'asset embed ...

    ?

  17. Salut,
    C'est un peu plus compliqué en fait, les images sont des PNG qui proviennent d'un serveur (dynamique, donc pas embeddable) et en plus, j'ai bidouillé le renderer pour qu'il puisse allez stocker et chercher les images dans une base SQLite en mode déconnecté :)

    Fabien

  18. Ok pour tout ça mais si je voulais simplement afficher mon icone à partir de mon répertoire assets local ?
    La question est en fait comment formater l'objet de retour de myIconFunction pour qu'il affiche la bonne icône à chaque ligne ?

    Et merci pour ta réactivité :)

  19. Ok, alors soit tu déclares tes Embed et tu renvoies la variable Class depuis l'iconFunction, soit tu fais pour que l'iconFunction renvoie l'url de ton icône (même locale)

  20. Oui c'était ça l'idée : ne pas avoir à déclarer l'embed + 1 var pour chaque item.
    Mais mon problème est que je récupère bien l'url locale ex. ./assets/picto/mon_picto.png mais c'est juste la chaîne qui va être retournée à mon iconFunction. J'imagine qu'avec le @Embed il doit y avoir un mécanisme un peu plus complexe qui va réellement récupérer l'icône ;-)

  21. Oui, quand c'est Embed, tu renvoies la variable de type Class qui a été "remplie" au moment de la compilation avec l'icône.

  22. Pour mon cas, en renvoyant directement la chaîne de type string à mon iconFunction ça passe bien.
    Merci pour tes explications.

  23. Avec flex 4.5.1, le défilement du composant List est beaucoup moins fluide sur mon Galaxy S qu'avec flex 4.5
    Adobe a alourdi le composant ?

  24. Salut,
    non, il n'a pas été alourdi, vérifie que tu testes bien en release

    Fabien

  25. je suis bien en release, ils ont corrigé des bugs, c'est peut-être pour ça que ça ralenti.
    si la liste est longue, le défilement saccade.

  26. Pareil pour moi sur un HTC Desire. J'irai pas jusqu'à dire que le défilement saccade mais il est sans aucun doute moins fluide qu'une liste native Android. Et pas besoin d'avoir une longue liste : il suffit qu'elle dépasse de quelques items la hauteur de l'écran.

  27. J'ai trouvé le soucis, c'était de ma faute.

  28. C'est toujours moins fluide quand même.

  29. dans flex 4.6, je ne trouve plus topSeparatorColor dans drawBackground

    on dirait que cela n'est pas prévu pour pouvoir être modifié par du CSS. Si tu regardes le code de LabelItemRenderer (ligne 873), tu as:
    // separators are a highlight on the top and shadow on the bottom
    topSeparatorColor = 0xFFFFFF;
    topSeparatorAlpha = .3;
    bottomSeparatorColor = 0×000000;
    bottomSeparatorAlpha = .3;
    Donc tu auras toujours une ligne noire et une ligne blanche. Ou alors, il faut qu'au lieu d'utiliser LabelItemRenderer, tu crée une classe qui hérite de LabelItemRenderer et qui redéfinit la méthode drawBackground.

  30. c'est dans la fonction drawBorder

  31. J'utilise dans mon application un stageWebView et j'ai l'impression que c'est lui qui ralentit le défilement de mon composant List sur Android depuis Flex 4.5.1

  32. En passant la frameRate à 48 c'est plus fluide sur SGS.

  33. svp est ce qu'il serait possible de mettre le labelfield en dessous de l'iconfield ?
    merci bien

  34. Salut,
    Il faut faire ton propre itemRenderer si tu veux avoir un texte de taille variable (multi-ligne) puis un texte simple en dessous. Pour éviter de répéter le code, tu fais un simple héritage sur l'itemRenderer de base puis tu redéfinis des méthodes comme layoutContents.
    Petite explication dans cet article où je fais exactement cela:
    http://www.flex-tutorial.fr/2011/08/27/air-mobile-application-tweetdeck-8-creation-dun-itemrenderer-pour-lobjet-tweet/

    Fab

  35. merci, mais j'ai un autre petit problème. je ne sais pas où je vais mettre ce code

    et vu que moi je veux mettre mon labelfield juste en dessous de la photo, je ne sais pas à quoi cet extrait de code va me servir et je ne le comprends pas très bien

    var hasLabel:Boolean = labelDisplay && labelDisplay.text != "";
    var hasMessage:Boolean = messageDisplay && messageDisplay.text != "";

    if (hasLabel && hasMessage){
    var labelY:Number = labelDisplay.y;
    var messageY:Number = messageDisplay.y;
    messageDisplay.y = labelY;
    labelDisplay.y = labelY + messageDisplay.height;

    ps: je suis nouveau sous flex

  36. Salut,
    si tu veux mieux comprendre, télécharge les sources du projet:
    http://www.flex-tutorial.fr/wp-content/uploads/tweetdeck/TweetDeck-3.fxp

    Fabien

  37. merci fabien !!!!!!!!!!!

  38. comment modifier l'entête d'un panel sous flex et y ajouter à cette entête un bouton du style"+"?
    tout en sachant que je suis sous version mobile!!!!!!!!!!!
    merci d'avance

  39. svp

  40. erreur de frappe ....... merci fabien

  41. Et stp excuses moi, jai plutot voulu dire augmenter la taille de l'entête du panel sous version mobile!!!

  42. @fab: La prochaine fois, utilise http://bit.ly/HezImC ;-)

  43. bonjour à tous,
    en ce moment je travaille sur le développement d'une application qui va être sur les tablettes. Et je la développe sous "flex"; svp j'aimerai savoir comment générer un appui long (long press) avec flex pour certaines fonctionnalités de mon application.

  44. Salut andy,
    Ca n'existe pas de base mais il est facile de le faire en quelques lignes de code. Ce projet devrait te donner les billes nécessaires:
    https://github.com/fljot/Gestouch

    Fabien


Leave a comment

(required)

Aucun trackbacks pour l'instant