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!
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.
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
. 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 !






