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

26juil/100

Lupo Manager, Lupo Translater et Lita en open source

J'en avais parlé il y a quelques semaines, David Deraedt a mis à disposition gratuitement ses applications Lupo Manager, Lupo Translator et Lita:

Lupo Manager et Lupo Translator disponibles gratuitement: à télécharger !

Il parlait aussi de dévoiler le code source de ces applications. Et bien, c'est chose faite! Vous pouvez télécharger les librairies et les projets sur ce billet de son blog:

Lita and Lupo now officially open source: come and join us!

Ce code est propre et de bonne qualité, n'hésitez pas à vous en inspirer!

25juil/100

Flex 4 – Création d’une Skin pour le composant HSlider

Prenons tout d’abord le composant HSlider de base de Flex 4 :

spark-4

Comme on l’a vu dans l'article précédent, dans la déclaration des SkinPart, on a "thumb" et "track" qui sont des boutons. On va laisser de côté, pour cet exemple, la Skin du DataTip. Pour créer une Skin, on va donc créer deux Skins de Button, un peu comme on l’a fait dans l’exemple précédent.
Pour la "track", on va simplement créer un rectangle arrondi avec un remplissage et une ombre portée. Ce fichier s’appellera MyTrackSkin.mxml:

<?xml version="1.0" encoding="utf-8"?>
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009"
        xmlns:s="library://ns.adobe.com/flex/spark"
        xmlns:mx="library://ns.adobe.com/flex/mx">
  <s:states>
    <s:State name="up" />
    <s:State name="over" />
    <s:State name="down" />
    <s:State name="disabled" />
  </s:states>
  <s:Rect radiusX="2" radiusY="2" top="0" right="0" bottom="0"
          left="0" height="3">
    <s:fill>
      <s:SolidColor color="0xFF3300"/>
    </s:fill>
    <s:stroke>
      <s:SolidColorStroke color="0x3E3E3E" weight="1" />
    </s:stroke>
    <s:filters>
      <s:DropShadowFilter blurX="5" blurY="5"
                          alpha="0.32" distance="2" />
    </s:filters>
  </s:Rect>
</s:Skin>

Pour la "thumb", on va simplement créer une ellipse avec un remplissage. Ce fichier s’appellera MyThumbSkin:

<?xml version="1.0" encoding="utf-8"?>
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009"
        xmlns:s="library://ns.adobe.com/flex/spark"
        xmlns:mx="library://ns.adobe.com/flex/mx">
  <s:states>
    <s:State name="up" />
    <s:State name="over" />
    <s:State name="down" />
    <s:State name="disabled" />
  </s:states>
  <s:Ellipse width="12" height="12">
    <s:fill>
      <s:SolidColor color="0xFF33FF"/>
    </s:fill>
    <s:stroke>
      <s:SolidColorStroke color="0x3E3E3E" weight="1" />
    </s:stroke>
  </s:Ellipse>
</s:Skin>

Il ne nous reste plus qu’à créer la Skin du Slider à proprement parler. Celle-ci sera constituée de nos deux composants. Notez leurs propriétés « id », liées aux SkinPart que l’on a décrit plus haut :

<?xml version="1.0" encoding="utf-8"?>
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009"
        xmlns:s="library://ns.adobe.com/flex/spark"
        xmlns:mx="library://ns.adobe.com/flex/mx">
  <fx:Metadata>
    [HostComponent("spark.components.HSlider")]
  </fx:Metadata>
  <s:states>
    <s:State name="normal" />
    <s:State name="disabled" />
  </s:states>

  <s:Button id="track" left="3" right="3" top="3" bottom="3" skinClass="MyTrackSkin"/>
  <s:Button id="thumb" top="0" bottom="0" width="12" height="5" skinClass="MyThumbSkin" />
</s:Skin>

Il ne nous reste plus qu’à donner notre Skin à notre composant dans l’application Flex principale :

<?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">
  <s:layout>
    <s:VerticalLayout horizontalAlign="center" verticalAlign="middle"/>
  </s:layout>
  <s:HSlider skinClass="MySliderSkin" width="150"/>
</s:Application>

Voici le résultat de notre Skin très simpliste :

spark-5

25juil/100

Flex 4 – Le lien "Composants" entre Skin et Composant

Jusqu’ici, on a vu deux types de contrat entre le composant et sa Skin : States et Données. Le dernier est le lien "Composant". Il faut tout d’abord comprendre qu’un composant peut-être défini par un ensemble de SkinPart (morceaux de Skin). Par exemple, pour une barre de défilement, on a :

  • Le bouton ascendant
  • Le bouton descendant
  • Le curseur
  • La zone dans laquelle le curseur va se déplacer (« track »)

Dans le cas du Button, il n’y a qu’une SkinPart : le label. C’est la seule partie dont le Button a besoin. C’est par ces SkinPart que l’on va établir le contrat "Composants".

Définition des SkinPart dans les composants Flex 4

Ces SkinPart sont aussi définies par annotation, juste au dessus des variables concernées. Par exemple, dans la classe spark.components.supportClasses.ButtonBase, on peut trouver :

[SkinPart(required="false")]

    /**
     *  A skin part that defines the label of the button.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public var labelDisplay:TextBase;

Dans cet exemple, la SkinPart est définie comme non requise. Cela nous a permis de pouvoir créer la Skin sans même connaître cette notion de SkinPart. Cela pourrait ainsi nous permettre de réaliser un bouton complètement graphique, sans texte.
Pour remplir le contrat, la Skin doit déclarer un composant du type TextBase (dont Label hérite) avec comme identifiant "labelDisplay". Dans ce cas-là, le composant hôte (le Button) va reconnaître la SkinPart et lui donner automatiquement la propriété "text".

On aura donc dans MyButtonSkin :

<s:Label id="labelDisplay" color="0x131313" textAlign="center"
           verticalAlign="middle"
           horizontalCenter="0" verticalCenter="1"
           left="12" right="12" top="6" bottom="6"
           />

On a plus de Data Binding entre le Label et le "hostComponent". Au lieu de cela, on lui donne comme "id", labelDisplay. Le composant Button fera ensuite le travail pour vous et va injecter sa propriété "label" dans le composant "labelDisplay".
Ce lien "Composant" va bien plus loin, en vous permettant de lier les comportements des composants à la Skin. Par exemple, pour le composant Slider, on a trois SkinParts (tous facultatifs) :

  • Le curseur
  • La zone dans laquelle le curseur va se déplacer (« track »)
  • Le DataTip (tooltip indiquant la valeur sélectionnée)

Voici les déclarations dans les classes TrackBase et SliderBase, dont les Slider héritent :

[SkinPart(required="false")]
public var thumb:Button;
[SkinPart(required="false")]
public var track:Button;
[SkinPart(required="false", type="mx.core.IDataRenderer")]
public var dataTip:IFactory;

Contrairement au bouton, dans lequel la zone interactive se résume au bouton tout entier, ici on a trois composants distincts, qui réagissent différemment aux actions utilisateur. Le composant « curseur » peut ainsi être déplacé par le clic de l’utilisateur.

Dans le cas du Slider, l’avantage de la liaison composant n’est pas de récupérer de la donnée, mais bien de pouvoir utiliser les évènements qui vont être ajoutés sur ces composants. Cela vous permet de profiter de la classe Slider de base, en modifiant uniquement son style graphique, et pas son comportement.

Pour bien illustrer ce comportement, on va créer une Skin de Slider.

Remplis sous: Flex 4, Skin Aucun commentaire
24juil/1012

DataFilterLib – Pagination de données filtrées (ArrayCollection avec filterFunction)

On m'a posé une question très intéressante sur la DataFilterLib:

Cedric
Bonjour moi j'aurais une petite question :
Comment intégrer les fonctionnalités de filtrage sur une dataGrid dont les données sont paginées (à l'aide du paginateur disponible sur ce site)?

En effet, la DataFilterLib permet de filtrer des données côté client selon plusieurs critères, de manière déclarative (MXML) ou dynamique (AS). Elle permet, pour simplifier, de donner plusieurs filterFunction à un ArrayCollection.

Et pour la pagination (côté client), le plus simple et le plus effectif est d'utiliser aussi une filterFunction sur une ICollectionView, un ArrayCollection par exemple. La question est alors, comment combiner les deux?

Application de test

Voilà le code utilisé pour tester l'application. Le dataProvider se trouve dans un fichier à part pour faciliter la lecture:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" height="100%"
                paddingRight="20" xmlns:filter="com.fnicollet.datafilter.filter.*"
                xmlns:pagination="pagination.*">
  <mx:Script>
    <![CDATA[
      import pagination.event.PaginateEvent;
      import com.fnicollet.datafilter.filter.DataFilterType;
      import com.fnicollet.datafilter.filter.DataFilterSingleValueOperator;

      [Bindable]
      private var _startIdx:int = 0;
      [Bindable]
      private var _endIdx:int = 999999999;

      private function pageChangeHandler(e:PaginateEvent):void {
        var page:int = e.index;
        _startIdx = (page * e.itemsPerPage) + 1;
        _endIdx = Math.min((_startIdx + e.itemsPerPage - 1), e.itemsTotal);
        //txt.text = "Results: " + String(_startIdx) + " - " + String(_endIdx) + " of " + e.itemsTotal;
      }
    ]]>
  </mx:Script>

  <mx:Script source="/data/StateData.as"/>
  <filter:DataFilterSet id="filterSet" data="{statesData}">
    <filter:dataFilterParameters>
      <filter:DataFilterParameters id="simpleParam" filterType="{DataFilterType.SINGLE_VALUE}"
                                   filterKeys="state"
                                   filterOperator="{DataFilterSingleValueOperator.LIKE}"
                                   filterValues="{stateInput.text}"/>
    </filter:dataFilterParameters>
  </filter:DataFilterSet>

  <mx:Label text="Filter By State Name (Contains)" fontSize="14" fontWeight="bold"/>
  <mx:TextInput id="stateInput"/>
  <mx:Label text="Pagination over the filtered elements" fontSize="14" fontWeight="bold"/>
  <pagination:PageSelector id="paginator" itemsPerPage="6" rangeCount="6"
                           itemsTotal="{statesData.length}" selectedIndex="0"
                           pageChange="pageChangeHandler(event)"/>
  <mx:Label text="Filtered Data" fontSize="14" fontWeight="bold"/>
  <mx:DataGrid rowCount="7" dataProvider="{statesData}" width="100%">
    <mx:columns>
      <mx:DataGridColumn dataField="state" headerText="State Name"/>
      <mx:DataGridColumn dataField="sales" headerText="Sales"/>
      <mx:DataGridColumn dataField="employees" headerText="Number of Employees"/>
      <mx:DataGridColumn dataField="population" headerText="Population"/>
    </mx:columns>
  </mx:DataGrid>

  <mx:Label text="Unfiltered Data" fontSize="14" fontWeight="bold"/>
  <mx:DataGrid rowCount="8" dataProvider="{statesData.source}" width="100%">
    <mx:columns>
      <mx:DataGridColumn dataField="state" headerText="State Name"/>
      <mx:DataGridColumn dataField="sales" headerText="Sales"/>
      <mx:DataGridColumn dataField="employees" headerText="Number of Employees"/>
      <mx:DataGridColumn dataField="population" headerText="Population"/>
    </mx:columns>
  </mx:DataGrid>

</mx:Application>

Un composant générique de pagination est intégré et met seulement à jour 2 variables Bindable. Il n'effectue encore aucun filtrage:

Composant Flex – Interface de Pagination générique

Premier test: Ajouter un nouveau filtre pour la pagination

Dans un premier temps, la première idée que j'ai eu fut de simplement ajouter un nouveau filtre, se comportant comme un filtre de type Interval, dont l'intervalle irait entre l'index de mon premier élément de page et le dernier élément.

Pour créer ce nouveau filtre, qui n'existe pas par défaut, il suffit de créer une classe qui hérite de "DataFilterInterval": DataFilterIntervalPagination. Pour l'ajouter en tant que filtre, il suffit d'utiliser la technique que j'expose dans cet article:

DataFilterLib – Utilisation de filtres personnalisés

Pour une utilisation dans une application, il suffit ensuite de donner:

<filter:DataFilterParameters filterClass="{DataFilterIntervalPagination}"
 id="intervalPagination" filterValues="{[_startIdx, _endIdx]}"/>

Et déjà, là, premier obstacle, la méthode de filtrage de base. Pour simplifier, la DataFilterLib boucle sur la méthode "apply" de chaque filtre dont la signature est la suiavnte:

public function apply(item:Object):Boolean {

On prend ensuite chaque résultat et si au moins un d'entre eux est false, l'objet est filtré. Pour les filtres sur valeur et sur intervalle, on cherche à comparer une certaine propriété de l'item (donnée par le paramètre "filterKeys"). En pseudo-code, on a :

public function apply(item:Object):Boolean {
 var value:String = item[filterKey];
 if (value == filterValue){
  return true;
 }
 return false;
}

On fait ici la distinction par rapport à une propriété de l'objet. Seulement dans notre cas, on ne veut pas comparer une des propriétés de l'objet mais bien la position de l'objet dans le dataProvider. Si son index est dans la page courante on garde l'objet, sinon on le filtre.

23juil/100

AIR pour Android – Vidéo conférence en 30 lignes de code avec LiveCycle Collaboration Services

Christophe Coenraets est on fire en ce moment et sort une application Air pour Android presque tous les jours :P . La dernière est une application de chat vidéo utilisant le service LiveCycle Collaboration Services.
Pour ceux qui ne connaissent pas LCCS (anciennement AFCS, anciennement Cocomo), vous trouverez plus d'informations sur le site officiel:

LiveCycle Collaboration Services @ Adobe

Pour résumer, LCCS propose d'utiliser des services Adobe en mode hébergé (SaaS). LCCS est aussi accompagné d'un SDK complet permettant d'intégrer facilement un chat vidéo, un tableau blanc, un partage de fichiers, … Il permet ainsi de gérer facilement les concepts de "room" et d'utilisateur. Beaucoup de traitement étant fait server-side par les systèmes de gestion sans que vous le sachiez.

Bref, LCCS était un projet qui était déjà intéressant pour du Desktop mais grâce à Air pour Android, (qui est une des prochaines versions de Air et pas un nouveau SDK), vous pouvez profiter de base du SDK livré avec LCCS.

Et c'est ce que fait CC dans sa vidéo de chat vidéo sur Android:

Comme je le disais un peu plus haut, LCCS vient avec un SDK complet, qui permet de s'affranchir de beaucoup de code. Dans le cas de cette application, seules 30 lignes de code on été nécessaires pour initier la connexion à la conférence et afficher un pod vidéo:

<?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:cs="AfcsNameSpace" currentState="logon" fontSize="28">

 <fx:Script>
 [Bindable] private var roomURL:String = "http://connectnow.acrobat.com/YOUR_ROOM_NAME";

 protected function connect():void {
  auth.userName = userName.text;
  currentState = "default";
  session.login();
 }
 </fx:Script>

 <s:states>
  <s:State name="default"/>
  <s:State name="logon"/>
 </s:states>

 <fx:Declarations>
  <cs:AdobeHSAuthenticator id="auth"/>
 </fx:Declarations>

 <s:TextInput id="userName" includeIn="logon" top="200" horizontalCenter="0"/>
 <s:Button label="Connect" click="connect()" includeIn="logon" top="250" horizontalCenter="0" height="50" width="150"/>

 <cs:ConnectSessionContainer id="session" roomURL="{roomURL}" authenticator="{auth}" autoLogin="false" width="100%" height="100%" includeIn="default">
  <cs:WebCamera top="10" left="10" bottom="10" right="10"/>
 </cs:ConnectSessionContainer>

</s:Application>

Voilà de quoi donner des idées !