Dans le dernier tutorial, on a vu comment créer un itemRenderer pour les Tweet. Avec quelques hacks pour combler un bug de Flex mais cela fonctionnait quand même:
AIR Mobile – Application TweetDeck (8) – Création d'un itemRenderer pour l'objet Tweet
Avant de continuer, on va essayer de faire pour que notre itemRenderer ait la tête de celui de TweetDeck:

Tout d'abord, on va changer la couleur du fond. Pour cela, on va utiliser une propriété CSS d'IconItemRenderer:"alternatingItemColors". Dans notre cas, les couleurs sont toujours les mêmes, il nous suffit donc de donner une couleur.
Dans le code de l'application principale "TweetDeck.mxml", rajoutez un tag "Style":
<?xml version="1.0" encoding="utf-8"?>
<s:ViewNavigatorApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" firstView="views.TweetDeckHomeView">
<fx:Style>
@namespace s "library://ns.adobe.com/flex/spark";
s|IconItemRenderer{
alternatingItemColors:#565556;
}
</fx:Style>
</s:ViewNavigatorApplication>
Le rendu:

Pour la couleur du texte de la deuxième ligne, on va utiliser le style CSS "color" qui s'applique au labelDisplay (dans notre cas, en bas):
s|IconItemRenderer{
alternatingItemColors:#565556;
color:#B5B6B5;
}
Maintenant, il nous faut afficher le texte en blanc et pas en noir. En temps normal, il suffirait de rajouter des styles sur IconItemRenderer mais avec notre hack pour l'affichage du texte HTML, on ne peut plus. On va donc indiquer cela dans le contenu HTML. Dans la méthode formatMessage de TweetRenderer.as, remplacez donc la dernière ligne par:
return "<font size=\"" + fontSize + "\" face=\"Arial\" color=\"#FFFFFF\">" + value.replace(urlPattern, "<a href='$1$2$3'>$1$2$3</a>") + "</font>";
Rendu:

Maintenant, les liens doivent aussi être en #B5B6B5. Cela se passe dans la construction de notre StyleSheet dans TweetRenderer.as:
var styles:String = "a {color: #B5B6B5;}";
Rendu:

Maintenant, un petit problème d'ordre esthétique, lorsque les images (icônes) sont en train de charger, elles ne prennent aucune place.
Dans le dernier tutorial, on a vu comment laisser Flash Builder nous créer les classes nécessaires à l'appel au service Twitter:
AIR Mobile – Application TweetDeck (6) – Configuration des appels HTTP au service Twitter
Dans ce tutorial, on va créer une liste et réaliser l'affichage d'une liste de Tweet récupérés par le service.
Création de la liste
Rendez-vous dans la vue TweetDeckHomeView.mxml et ajoutez un tag "List" à la place du Rect violet:
<comps:ColumnHeaderIndicator width="100%" height="40" />
<s:List width="100%" height="100%">
</s:List>
<comps:BottomBar width="100%" height="80" />
Appel du service Twitter
Si vous utilisez la vue Design de Flash Builder, vous pourriez glisser directement le service sur votre List pour créer le code d'appel. Mais on va essayer de ne pas passer par la vue Design pour mieux comprendre. Rendez-vous dans la vue "Data/Services" puis faites un clic droit > Generate Service Call:

Boum, Flash Builder vient de vous générer du code dans votre vue:
<fx:Script>
<![CDATA[
protected function search(q:String):void
{
searchResult.token = twitter.search(q);
}
]]>
</fx:Script>
<fx:Declarations>
<s:CallResponder id="searchResult"/>
<twitter:Twitter id="twitter"/>
</fx:Declarations>
Les objets CallResponder et AsyncToken
Vous avez donc une instance du service Twitter et un objet CallResponder. Si vous ne connaissez pas la classe CallResponder, c'est une classe bien utile. En fait, au lieu d'aller ajouter des écouteurs d'évènement directement sur le service pour écouter "result" et "fault", vous allez déporter cela sur l'instance de CallResponder. C'est votre CallResponder qui va appeler le service Twitter et récupérer un objet "token" de type AsyncToken. Celui-ci permet de repérer la transaction HTTP de manière unique.
Le framework Flex propose en standard, un composant List, qui réagit aux évènements touch pour une utilisation sur mobile. A cette liste, vous pouvez coupler un renderer comme par exemple un renderer de base de Flex, IconItemRenderer qui vous permet d'afficher une image + 2 lignes de texte pour chaque élément.
Mais il y a un autre type de rendu assez populaire sur mobile, qui est de créer une liste à "sections". Par exemple la liste de contacts sous Android ou iOS. Les contacts sont arrangés par ordre alphabétique et rassemblés par la première lettre de leur prénom:

Les sections ne sont pas cliquables, elles servent juste de décoration. Elles ont le plus souvent une taille différente de celle des items. Je ne suis pas un cador du développement natif mais je pense qu'il y a un composant tout prêt pour ce genre d'utilisation. En Flex, il vous faudra le faire vous-même.
Mais comme d'habitude, certains se sont posé la question avant vous (désolé) et on trouvé une solution. Voici quelques liens qui vous seront utiles:
http://corlan.org/2011/07/04/creating-flex-mobile-section-lists/
http://corlan.org/2011/07/11/creating-flex-mobile-lists-part-ii-using-virtualization/
http://www.tink.ws/blog/flex-4-excludeselectionlist/
http://code.google.com/p/tink/source/browse/trunk/flex4.5/spark/src/ws/tink/spark/controls/ExcludeSelectionList.as
http://flexponential.com/2011/06/27/extending-labelitemrenderer-to-look-like-itunes-on-the-ipad/
Voilà, je vous ai bien préparé le travail mais on va aller plus loin en regardant les techniques utilisées.
La technique générale
Pour intégrer ces sections, et que le rendu de la liste soit cohérent, on intègre ces sections directement dans le dataProvider de la liste. Au milieu de vos objets Contact, vous aurez donc d'autres items qui sont là pour la déco. Il faut ensuite déterminer dans votre liste quels sont ces éléments et leur donner un rendu différent.
Le composant ExcluseSelectionList
Le (très bon) blogger de tink.ws propose une implémentation de List.as (par héritage) qui permet d'indiquer au composant quels éléments sont "non-sélectionnables". Vous disposez donc de plusieurs propriétés pour définir cette restriction:
- excludeType / excludeTypes : Permet de donner une liste de type AS3 à exclure (ex: la classe Header)
- excludeIndex / excludeIndices : Permet de donner une liste d'index à exclure (ex 1, 10, 30)
- excludeFunction : Permet de passer une fonction qui va renvoyer un Boolean pour indiquer s'il faut exclure ou pas l'élément. Un peu comme une filterFunction.
Ce composant va se révéler très pratique mais on peut voir que l'itemRenderer créé par flexponential.com contient un petit "trick" qui permet de se passer de cette restriction. En effet, il se contente de ne pas passer l'itemRenderer en état "sélectionné" s'il est sur un header. Un couplage des 2 solutions semble le meilleur compromis.
Utilisation des itemRenderer
C'est la solution la plus naturelle et la plus élégante d'un point de vue code. Suivant l'item passé à l'itemRenderer, on va modifier le rendu.On peut faire ça facilement en MXML mais il vaut mieux le faire en AS3 pour avoir de meilleurs perfs sur mobile.
L'exemple de flexponential explique bien la technique:
http://flexponential.com/2011/06/27/extending-labelitemrenderer-to-look-like-itunes-on-the-ipad/
Ici, on utilise bien la propriété itemRenderer et pas une itemRendererFunction. C'est encore mieux car l'utilisation d'une itemRendererFunction empêche la ré-utilisation des renderers et devient donc moins performante en RAM. Dans l'exemple, le composant MusicLabelItemRenderer est taillé pour cette application puisqu'il connait le type d'item qu'il va afficher. Il peut donc facilement faire la distinction entre un item et un header. Très pratique si vous connaissez votre liste, moins si vous voulez être générique.

En l’occurrence, le renderer est assez complexe puisqu'il permet d'afficher les éléments de manière horizontale avec une ligne séparatrice.
La problématiques des layouts
Voici un problème que je n'avais même pas envisagé pendant la rédaction de cet article. De base, une List utilise un "layout" qui est un VerticalLayout. On obtient donc une liste verticale. Mais sur de plus grands écrans, vous voudrez peut-être utiliser un layout qui vous permet d'afficher plusieurs éléments par ligne, comme un TileLayout:


Si vous modifier votre itemRenderer, vous n'aurez pas un header sur une ligne complète, juste sur la taille d'un item. Le résultat sera donc mauvais. Les billets de corlan.org peuvent vous permettre de résoudre cette problématique en faisant en sorte que votre header prenne toute la largeur. C'est un cas assez exceptionnel mais au moins vous êtes prévenus
.
Conclusion
Avec ces différents articles, vous avez assez d'informations pour pouvoir créer votre liste. Certaines subtilités comme le layout ou la manière par laquelle on rend les éléments non-sélectionnables sont optionnels, essayez de faire au plus simple.
Dans un futur tutorial, je vais peut-être partager un exemple complet de liste avec groupement avec en bonus le composant de scroll rapide comme sur Android. Pour ceux qui ne voient pas, c'est une liste alphabétique qui vient au dessus de la liste, permettant d'aller directement à la bonne lettre. Très pratique quand on a des dizaines / centaines d'éléments
.
Dans l'exemple Flex précédent, on utilisait un itemRenderer pour afficher une donnée calculée dans une colonne spécifique. Dans cet exemple, on va voir comment couvrir toutes les colonnes avec un itemRenderer. On va ainsi afficher un composant Flex PieChart pour afficher la donnée du champ "detail" de notre donnée. Chaque ligne contient des informations détaillées sur les ventes de chaque représentant, qui est représenté par une part du PieChart.
Voici le code de l'application Flex principale:
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
private var dpHierarchy:ArrayCollection= new ArrayCollection([
{name:"Barbara Jennings", region: "Arizona", total:70, children:[
{detail:[{amount:5},{amount:10},{amount:20},{amount:45}]}]},
{name:"Dana Binn", region: "Arizona", total:130, children:[
{detail:[{amount:15},{amount:25},{amount:35},{amount:55}]}]},
{name:"Joe Smith", region: "California", total:229, children:[
{detail:[{amount:26},{amount:32},{amount:73},{amount:123}]}]},
{name:"Alice Treu", region: "California", total:230, children:[
{detail:[{amount:159},{amount:235},{amount:135},{amount:155}]}
]}
]);
]]>
</mx:Script>
<mx:AdvancedDataGrid id="myADG"
width="100%" height="100%"
variableRowHeight="true">
<mx:dataProvider>
<mx:HierarchicalData source="{dpHierarchy}"/>
</mx:dataProvider>
<mx:columns>
<mx:AdvancedDataGridColumn dataField="name" headerText="Name"/>
<mx:AdvancedDataGridColumn dataField="total" headerText="Total"/>
</mx:columns>
<mx:rendererProviders>
<mx:AdvancedDataGridRendererProvider
dataField="detail"
renderer="ChartRenderer"
columnIndex="0"
columnSpan="0"/>
</mx:rendererProviders>
</mx:AdvancedDataGrid>
</mx:Application>
Vous pouvez personnaliser l'apparence et le comportement des cellules d'un composant AdvancedDataGrid en créant des item renderers et des item editors spéciaux. Vous pouvez de la même manière assigner des itemRenderer à un composant AdvancedDataGrid que vous le faîtes pour le composant DataGrid.
Tous les tutoriaux sur les itemRenderer Flex
Le composant AdvancedDataGrid apporte de nouvelles fonctionnalités aux itemRenderer. Voici ce que vous pouvez faire avec l'AdvancedDataGrid que vous ne pouviez pas faire avec une DataGrid:
- Créer des lignes ou des colonnes non-associées avec de la donnée du dataProvider. Par exemple, vous pouvez créer des lignes Summary depuis le dataProvider.
- Couvrir plusieurs colonnes avec un renderer
- Utiliser de multiples renderers dans une même colonne. Par exemple, quand vous affichez de la donnée hiérarchique, vous pouvez utiliser différents renderer suivant le niveau de la ligne dans la hiérarchie.
Utilisation simple d'ItemRenderer dans une AdvancedDataGrid
Pour utiliser un itemRenderer dans un composant AdvancedDataGrid, vous ne devez pas assigner l'itemRenderer à une colonne mais au composant AdvancedDataGrid lui-même en utilisant la propriété "rendererProviders".
Le code MXML suivant assigne un itemRenderer à la colonne "Estimate" (columnIndex=3):
<mx:AdvancedDataGrid>
<mx:columns>
<mx:AdvancedDataGridColumn dataField="Region"/>
<mx:AdvancedDataGridColumn dataField="Territory_Rep" headerText="Territory Rep"/>
<mx:AdvancedDataGridColumn dataField="Actual"/>
<mx:AdvancedDataGridColumn dataField="Estimate"/>
</mx:columns>
<mx:rendererProviders>
<mx:AdvancedDataGridRendererProvider
columnIndex="3"
columnSpan="1"
renderer="myComponents.EstimateRenderer"/>
</mx:rendererProviders>
</mx:AdvancedDataGrid>
La propriété rendererProviders contient un tableau (Array) d'instance de AdvancedDataGridRendererProvider. Chaque instance d'AdvancedDataGridRendererProvider définit les caractéristiques d'un itemRenderer. Dans le morceau de code juste au dessus, l'instance d'AdvancedDataGridRendererProvider spécifie qu'il utilise EstimateRenderer pour la colonne 3 (la première colonne a l'index 0), et le renderer s'étend sur une seule colonne.