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

25juin/100

Flex AdvancedDataGrid – Supprimer le séparateur dans les headers des colonnes non triables

Il y a souvent des commentaires sur flex-tutorial pour savoir comment contourner certains comportements par défaut de Flex. En voici un sur l'article Flex AdvancedDataGrid – Tri sur plusieurs colonnes (Sort et sortExpertMode):

Niko 

Hello!
Est il possible de supprimer le petit trait sur les colonnes non tri-ables pour permettre l'affichage de l'entête de colonne sur la totalité de la largeur ?
N.

Cela semble tout bête (voire même cela devrait être un comportement par défaut) mais la solution se révèle en fait assez difficile à mettre en œuvre. Etant toujours avide de défi, je me suis penché de plus près sur le problème.

Comment analyser les comportements de Flex

Dans un cas comme cela, ce n'est pas son propre code qu'il faut comprendre mais bien celui du framework Adobe Flex. Si vous savez déjà dans quelle direction chercher, vous pouvez aller chercher dans les livedocs Flex:

Flex 4 Livedocs

Les Livedocs sont honnêtement bien faites et complètes, vous trouverez un maximum d'information sur les classes de Flex.

Pour aller fouiller dans le code de Flex (et oui, c'est là que l'on se rend compte de la puissance du côté Open Source de Flex ;) ), vous pouvez simplement faire Ctrl-Click sur une classe ou un élément MXML dans Flex Builder. Cela va vous emmener directement dans la classe. Pour les fans des raccourcis clavier, appuyer sur F3 et FB vous emmènera dans la classe sur laquelle se trouve le curseur.

go-to-definitionParfois, vous n'aurez pas la classe sous les yeux ou alors son nom se trouvera dans un commentaire ASDoc. Par exemple, je cherchais au départ comment était déterminé l'itemRenderer d'une AdvancedDataGridColumn. Avec Ctrl+O, je trouve un assesseur sur "itemRenderer" donc l'ASDoc est la suivante:

/**
 *  @private
 *  Storage for the itemRenderer property.
 */
 private var _itemRenderer:IFactory;

 [Bindable("itemRendererChanged")]
 [Inspectable(category="Other")]

 /**
 *  The class factory for item renderer instances that display the
 *  data for each item in the column.
 *  You can specify a drop-in item renderer,
 *  an inline item renderer, or a custom item renderer component as the
 *  value of this property.
 *
 *  <p>The default item renderer is the AdvancedDataGridItemRenderer class,
 *  which displays the item data as text. </p>
 *
 *  @see mx.controls.advancedDataGridClasses.AdvancedDataGridItemRenderer
 */
 public function get itemRenderer():IFactory
 {
 return _itemRenderer;
 }

L'ASDoc me dit d'aller voir la classe "AdvancedDataGridItemRenderer". Pour cela, je ne peux pas faire un Navigate To, mais je peux cependant faire un "Open Type" (Ctrl+Shift+T). Contrairement à un "Open Resource" (Ctrl+Shift+R), qui va me montrer tous les fichiers (css, xml, as, mxml, …) présents dans les projets ouverts de mon Workspace, Open Type va me permettre d'ouvrir n'importe quelle classe.
open-type

Notez que vous pouvez utiliser des jokers "*" dans le champ de recherche. Ainsi, en entrant "*DataGrid*Renderer*", vous aurez toutes les classes contenant DataGrid et Renderer, ce peut vous permettre de trouver rapidement des classes dérivées.

Voilà, avec ces quelques raccourcis, vous pouvez aller fouiller rapidement dans le code de Flex.

Revenons à notre problème de header…

Après une 10aine de minutes de recherches, je trouve la classe "AdvancedDataGridHeaderRenderer" qui s'occupe de faire le rendu du header des colonnes uniquement. Notez qu'en utilisant un Open Type avec les bons mots-clés, je l'aurais trouvée directement :P .

On y trouve ainsi:

/**
 *  Specifies a custom sort item renderer.
 *  By default, the AdvancedDataGridHeaderRenderer class uses
 *  AdvancedDataGridSortItemRenderer as the sort item renderer.
 *
 *  <p>Note that the sort item renderer controls the display of the
 *  sort icon and sort sequence number.
 *  A custom header renderer must include code to display the
 *  sort item renderer, regardless of whether it is the default or custom
 *  sort item renderer.</p>
 */
 public function get sortItemRenderer():IFactory
 {
 return _sortItemRenderer;
 }

C'est bien cet itemRenderer, "sortItemRenderer" qui s'occupe de gérer le rendu du coin droit des headers de colonnes. Comme souvent, on va modifier le comportement de cette classe en héritant de cette classe et en surchargeant certaines de ses méthodes, vu qu'on ne va pas changer le code de Flex. On va donc créer une classe CustomHeaderRenderer qui hérite de AdvancedDataGridHeaderRenderer.

Pour commencer, j'ai juste tenté de surcharger le getter et de renvoyer null, dans tous les cas, pour voir ce que cela faisait:

package {
 import mx.controls.advancedDataGridClasses.AdvancedDataGridHeaderRenderer;
 import mx.core.IFactory;

 public class CustomHeaderRenderer extends AdvancedDataGridHeaderRenderer {
   public function CustomHeaderRenderer() {
     super();
   }

   override public function get sortItemRenderer():IFactory {
     return null;
   }

 }
}

Et je tente de l'intégrer dans mon application:

<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" viewSourceURL="srcview/index.html">

  <mx:Script>
    <![CDATA[
      import mx.collections.ArrayCollection;

      [Bindable]
      private var dpADG:ArrayCollection = new ArrayCollection([{Artist: 'Pavement', Album: 'Slanted and Enchanted', Price: 11.99}, {Artist: 'Pavement', Album: 'Brighten the Corners', Price: 11.99}, {Artist: 'Saner',
          Album: 'A Child Once', Price: 11.99}, {Artist: 'Saner', Album: 'Helium Wings', Price: 12.99}, {Artist: 'The Doors', Album: 'The Doors', Price: 10.99}, {Artist: 'The Doors', Album: 'Morrison Hotel',
          Price: 12.99}, {Artist: 'Grateful Dead', Album: 'American Beauty', Price: 11.99}, {Artist: 'Grateful Dead', Album: 'In the Dark', Price: 11.99}, {Artist: 'Grateful Dead', Album: 'Shakedown Street',
          Price: 11.99}, {Artist: 'The Doors', Album: 'Strange Days', Price: 12.99}, {Artist: 'The Doors', Album: 'The Best of the Doors', Price: 10.99}]);
    ]]>
  </mx:Script>
  <mx:ApplicationControlBar dock="true">
    <mx:CheckBox id="sortExpertModeCB" label="sortExpertMode?"/>
  </mx:ApplicationControlBar>
  <mx:AdvancedDataGrid width="100%" height="100%" sortExpertMode="{sortExpertModeCB.selected}"
                       dataProvider="{dpADG}">
    <mx:columns>
      <mx:AdvancedDataGridColumn dataField="Artist" sortable="false"
                                 headerRenderer="CustomHeaderRenderer"/>
      <mx:AdvancedDataGridColumn dataField="Album"/>
      <mx:AdvancedDataGridColumn dataField="Price"/>
    </mx:columns>
  </mx:AdvancedDataGrid>
</mx:Application>

Et voici le résultat:

custom-renerer-1Et voilà, mission accomplie :) . En 15 minutes, pas mal de recherches, un peu de test, on a réussi à "hacker" un comportement par défaut de Flex.

12mar/103

AdvancedDataGrid, Charting et OLAPDataGrid (datavisualization.swc) enfin GRATUIT!

En voilà une excellente nouvelle de la part d'Adobe (non, pas la sortie de Flash Player 10.1 :P ), la mise à disposition du SWC datavisualization.swc dans le SDK Open Source et sans restriction de licence.

Il y a 9 mois, je vous annonçais l'open-sourcing du Flex Data Visualization mais ici on parle bien de l'aspect gratuit de cette librairie. En effet, Open source ne veut pas forcement dire gratuit.

La nouvelle responsable produit sur Flex (Deepa Subramaniam) en a fait l'annonce lors du 360|Flex:

Flex advanced data visualization components now part of the FREE open source Flex SDK!

Pour résumer, vous n'aurez plus besoin d'une licence Flex Builder Professionnal pour utiliser les composants de la librairie datavisualization.swc. Parmi eux, on peut trouver:

  • AdvancedDataGrid: Composant qui va bien au delà de la DataGrid, avec de nouvelle possibilités d'itemRenderer, de bilan et de groupement (et bien d'autres)
  • Charting: Tous les graphiques possibles avec Flex (histogramme, bulles, etc.)
  • OLAPDataGrid: Peut servir pour des besoins spécifiques d'agrégation de données.

Je viens de vérifier sur le dernier "nightly build" de Flex 4, le swc est bien là dans le SDK Open Source:

dv

Télécharger le dernier build du Flex 4 SDK

Voila donc une excellente nouvelle pour la communauté Flex, puisque l'on peut maintenant réellement créer l'ensemble des applications entreprise que l'on souhaite sans débourser un sou (pour rappel, le SDK est gratuit, seul Flex Builder est payant). Le prix d'entrée pour un développeur se trouve du coup largement réduit. Pour rappel, voici la grille des prix Adobe:

  • Flex Builder 3 standard: 249$
  • Flex Builder 3 professional: 699$

Bon maintenant, on peut se demander à quoi va servir une licence Flex Builder Pro par rapport à une licence Flex standard. Pour l'instant je ne vois que le profiler et peut-être les wizards (même pas sûr et puis qui utilise les wizards ?^^). En regardant les derniers build Flex 3 SDK, on dirait que ce n'est que pour le SDK Flex 4. Mais bon, on peut compiler des applications Flex 3 avec le Flex 4 SDK donc peu importe.

Bref, vivement que Flex 4 et Flex Builder 4 sortent, il se font vraiment attendre maintenant :P

20sept/091

Flex ArrayCollection – FilterFunction avec plusieurs functions (Comparatif de 5 méthodes)

L'objet ArrayCollection est souvent utilisé comme dataProvider pour remplir des List, des DataGrid, des AdvancedDataGrid et bien d'autres. Pour faire simple, c'est une Array amélioré, qui propage des évènements lors de sa modification et permet donc d'avoir un DataBinding (liaison) entre cet objet et le dataProvider.

Fonctionnement basique de filterFunction sur ArrayCollection

L'objet ArrayCollection propose une propriété nommée "filterFunction" permettant de filtrer la liste selon certains critères de votre choix. Il faut pour cela assigner une fonction de callback qui va renvoyer un Boolean. La signature de la fonction filterFunction est la suivante:

public function filterFunction(item:Object):Boolean{
	// votre code
}

Quand Flex va faire une mise à jour du dataProvider (comme un refresh() sur l'objet ArrayCollection), pour chaque élément il va appeler cette fonction. Il va passer l'élément parcouru comme paramètre de cette fonction, c'est-à-dire le paramètre "item". Si cette fonction retourne "true", l'élément ne sera pas filtré et sera donc affiché. Si elle renvoie false, l'élément sera filtré et ne sera pas affiché.

Si vous voulez mettre à jour les éléments filtrés, il vous suffit de mettre à jour le dataProvider en appelant sa méthode myArrayCollection.refresh(). Pour ne filtrer aucun élément, mettez la propriété filterFunction à null.

Comment filtrer un ArrayCollection sur plusieurs critères ?

Avec des données parfois complexes, de multiples champs et parfois des types de données qui ne sont pas des types de base, vous allez peut-être vouloir créer un filtrage plus complexe, sur plusieurs champ ou sur plusieurs valeurs. Je vais exposer ici plusieurs méthodes dont une que j'ai mise au point moi-même. A vous de faire votre choix suivant la complexité de votre application ;) .

Méthode 1 – La méthode triviale (mais robuste)

Admettons que vous ayez maintenant une DataGrid ou ADG complexe et que vous vouliez filtrer les éléments sur plusieurs critères. Vous pouvez faire une fonction filterFunction avec un code interne complexe comme ceci:

public function filterFunction(item:Object):Boolean{
	var price:int = int(item.price);
	var product:String = item.product;
	if (price > 20 && product != "ColdFusion"){
		return true;
	}
	return false;
}

Cette méthode de filtre ne laisserai que les éléments donc le champ prix est supérieur a 20 et dont le nom n'est pas "ColdFusion". On filtre ici selon deux critères simples avec des valeurs fixes. On peut mettre les valeurs de comparaison (20 et ColdFusion) comme variable de la classe pour pouvoir être modifiées dynamiquement mais si par exemple vous voulez trier selon un autre champ, il vous faut créer une nouvelle filterFunction sur 3 champs et vous allez vite vous rendre compte que cette méthode n'est pas viable car trop robuste. Si on y réfléchit, le plus simple serait d'assigner plusieurs fonctions de type filterFunction. Or, ArrayCollection permet seulement d'assigner une seule filterFunction. Les méthodes suivantes tentent de résoudre ce problème.

Méthode 2 – Créer une sous-classe de ArrayCollection et ajouter une propriété filterFunctions

Voici une méthode que propose Cristian Rotundu sur son blog. Cette méthode consiste à créer un composant qui hérite d'ArrayCollection (baptisé ArrayCollectionExtended) pour pouvoir ajouter une propriété filterFunctions. Cette propriété prend en paramètre un Array (tableau) de fonction qui vont chacune filtrer la data. Si l'une de ces fonctions renvoie false pour un élément, il sera filtré.

Voici comment il est utilisé dans une application:

someArrayCollection.filterFunctions =
	[ 	filterByVendor, filterByPrice,
		filterByTime, filterByEventType,
		filterByKeywords
	];	

someArrayCollection.refresh();

Cette méthode fonctionne bien mais elle reste cependant assez contraignante. Tout d'abord, le fait d'hériter d'ArrayCollection est pratique dans ce cas mais si vous utilisez de nombreuses ArrayCollection dans votre application, il vous faudra remplacer toutes vos déclarations par ce nouveau type. Et si vous utilisiez déjà une classe héritant d'ArrayCollection, comme vous ne pouvez pas faire d'héritage multiple en AS3, il va falloir bricoler.

Voir l'exemple d'application de cette méthode

29août/094

Composant Flex – Interface de Pagination générique

Dans votre application Flex, vous pouvez être amené à afficher un gros volume de données. Pour une application de reporting par exemple, vous pourriez avoir des milliers voire des dizaines de milliers d'enregistrements.

Si vous essayez de les afficher tous à la fois dans une AdvancedDataGrid par exemple, votre application va devenir inutilisable car le composant AdvancedDataGrid n'est pas fait pour afficher autant de données. De plus, l'utilisateur aura beaucoup de mal à lire ces résultats, ne serait-ce que pour arriver à prendre la minuscule Scrollbar :P .

La solution la plus commune dans ce genre de cas est de créer une pagination, c'est-à-dire de séparer votre donnée en plusieurs pages. Je reviendrai plus tard sur ce blog sur la manière par laquelle gérer la pagination des AdvancedDataGrid / DataGrid.

En attendant, vous avez peut-être déjà votre propre système de pagination au niveau serveur et vous voulez l'implémenter au niveau client.

Voici donc un composant Flex qui va bien vous rendre service. Il permet de créer simplement une interface pour choisir la page à charger. Ce composant est entièrement configurable et peut être facilement modifié à votre guise (on va le voir plus loin).

Voici les paramètres que l'on peut configurer dans le composant Flex de base:

  • itemsPerPage: Nombre d'éléments par page (pour pouvoir calculer le nombre de pages)
  • rangeCount: Nombre de boutons de sélection de page à afficher à l'écran
  • itemsTotal: Nombre total d'éléments (cumul de toutes les pages)
  • selectedIndex: Page sélectionnée (automatique au clic sur le bouton mais il est aussi possible de le setter)

Deux évènements sont dispatchés par ce composant et notamment l'évènement "pageChanged" qui va vous permettre de savoir quand l'utilisateur a demandé une nouvelle page et ainsi de demander la nouvelle donnée.

Voir la page officielle de l'auteur du Paginator

Exemple d'application Flex utilisant le WAIPaginator

Flex Source Code Download: Télécharger le code source complet de l'application

This movie requires Flash Player 11

26août/090

Composant Flex – AdvancedDataGrid Auto Resizable (ajustement des colonnes)

Avec le composant AdvancedDataGrid de base, vous pouvez redimensionner les colonnes par un drag and drop des séparateurs de colonnes. Mais dans de nombreuses applications, comme par exemple l'Explorateur Windows ou le File Explorer d'Ubuntu, vous pouvez faire un double-clic sur un de ces séparateurs qui va agrandir la colonne pour qu'elle puisse afficher le plus grand élément. Ce comportement n'est pas disponible dans une AdvancedDataGrid par défaut.

La solution est comme souvent de créer un composant qui va hériter d'AdvancedDataGrid et de redéfinir certaines de ses méthodes.
Sameer Bhatt de la Adobe Flex Team a fait ces modifications et a crée pour cela un composant baptisé AutoResizableADG.

Voir la page officielle de l'auteur

Télécharger le composant AutoResizableADG.as

Exemple d'application

Flex Source Code Download: Télécharger le code source complet de l'application

This movie requires Flash Player 11