Développement mobile avec Flex
NOTE: Pour mieux comprendre les codes donnés dans ce tutoriel, vous devez avoir au moins quelques petites notions en ActionScript. Pour vous aider, voici une Introduction à ActionScript 3.0 (en anglais)
Au cours de ce tutorial, nous allons voir comment développer une simple application pour Android et Blackberry, en utilisant Adobe Flash Builder 4.5 et Flex SDK 4.5.1.
Adobe Flash Builder est un environnement de développement professionnel, basé sur Eclipse, conçu pour les développeurs qui souhaitent rapidement coder, tester et déployer des RIA multi-plateformes. Il permet à la fois une édition avancée de code MXML et ActionScript, ainsi qu’une mise en page visuelle et une définition des styles (CSS).
L’interet de développer une application mobile avec Flex est de pourvoir écrire un seul code, puis redistribuer l’application sur les différentes platformes mobiles Android et Blackberry (et iOS).
0 Aperçu du résultat final
Voici à quoi ressemblera l’application que nous allons développer: Un simple lecteur de flux RSS avec un splash screen.
1 Installation des SDK
Avant de se lancer dans le développement de notre application, nous allons avoir besoin d’installer et configurer Adobe Flash Builder 4.5 et les différents SDK nécessaires.
A Installation de Flash Builder 4.5
Le développement de notre application repose sur Flash Builder et Flex, vous devez donc télécharger une version d’essai ou acheter la version complète de Flash Builder 4.5.
Cependant, si vous êtes étudiant, Adobe vous offre une licence gratuite de Flash Builder 4.5; si cela vous intéresse, rendez-vous donc à cette adresse http://www.adobe.com/devnet-apps/flex/free/index.html
NOTE: après l’installation de Flash Builder 4.5, vérifiez que vous disposez bien de la version 4.5.1 de Flex qui apporte le support des plateformes Blackberry et iOS!
B Installation du SDK Android
Voici le lien à suivre pour l’installation et la configuration du SDK pour Android http://developer.android.com/sdk/index.html
C Installation du SDK Blackberry pour Adobe AIR
Si vous voulez développer pour les platformes Blackberry, il va vous falloir le SDK pour AIR. Pour plus de détails concernant l’installation du SDK et le simulateur, veuillez visiter http://us.blackberry.com/developers/tablet/adobe.jsp
2 Creation du projet
Maintenant que tout est prêt, nous allons lancer Adobe Flash Builder et créer un “Projet Flex Mobile”:Fichier > Nouveau > Projet Flex Mobile
Ensuite, nous donnons un nom à notre application, et veiller à utiliser le SDK par défaut:
Dans l’étape suivante, nous allons pouvoir choisir les différentes platformes cibles, dans notre cas, nous allons choisir les trois plateformes.
Concernant le modèle d’application, nous allons choisir “Application basée sur les vues” car notre application ne nécessite pas d’onglet et sera composée essentiellement de vues (ou pages).
Dans l’onglet “Autorisations”, on va définir les différentes autorisations à donner à notre application pour les platformes Android et Blackberry. Dans notre exemple, l’application aura besoin d’utiliser le réseau de l’appareil pour se connecter à Internet.
Pour définir ces autorisations pour les plateformes Android, nous choisissons “Google Android” et cochons “INTERNET” dans la liste des autorisations.
On fait de même pour les plateformes Blackberry:
Ensuite, dans les paramètres d’applications, on coche “Plein écran” et “Dimensionner automatiquement l’application pour différentes densités d’écran” et on laisse la résolution de l’application à 160 ppp (dpi en anglais). Nous n’allons pas détailler la notion de résolution au cours de ce tutoriel, car elle nécessite un article à elle seule!
3 Ajouter une source de données
Je m’excuse d’avance, mais j’étais en quelques sorte obligé de changer de machine, et du coup les captures d’écran qui suiveront ont été faites sur version en anglais de Flash Builder!
Flash Builder vous offre la possibilité d’ajouter une source de données ou un web service grâce à différents protocoles et technologies (http, SOAP, XML, BlazeDS…), soit pour alimenter votre application en données, ou pour interroger une base de données sur un serveur distant.
Pour cela, et pour plus de simplicité, nous allons utiliser l’assistant de Flash Builder pour ajouter notre source de données qui n’est au final qu’un fichier XML de flux RSS.
On choisit “URL”, et on saisie l’URL de notre fichier XML distant, puis on cliques sur “Invoke” et on Termine l’assistant.
L’assistant crée ensuite les fichiers et le code ActionScript nécessaires dans des répertoires bien particuliers:
- Services.NOM_DU_SERVICE
- valueObjects
Vous pouvez évidement modifier le code généré, mais je vous déconseille de le faire à moins si vous savez ce que vous faites!
Ensuite, nous allons configurer le type de retour de la méthode getData() afin de pouvoir l’utiliser en tant que dataProvider pour alimenter la liste des articles.
Pour ce faire, faites un clique-droit sur le nom de la méthode, puis “Configurer le type de retour”. On laisse l’assistant détecter le type automatiquement.
Suivant …
Suivant …
Dans cette dernière étape, on sélectionne l’entrée Item qui est un tableau, dans les deux menus déroulant. En effet, cette entrée est un tableau contenant les différents articles publiés.
Voilà! Notre source de données est maintenant configurée et prête à l’emploi. Passons aux choses rigolotes maintenant: la construction des différents blocs de notre application.
4 Vue principale
Maintenant que le projet et la source de données ont été crées et configurés, nous allons commencer le développement des différentes vues.
Flash Builder crée automatiquement l’arborescence de votre projet, ainsi tous vos développements se dérouleront au sein du répertoire src/.
Maintenant, ouvrez la vue MyRSS.mxml, cette vue est le point d’entrée de l’application; voyez-la comme la méthode main() dans un programme en C.
Nous allons ensuite remplacer le code pré-généré par celui-ci:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 < ?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"
xmlns:feed="services.feed.*"
applicationDPI="160"
creationComplete="init_creationCompleteHandler(event)"
splashScreenImage="@Embed('assets/header_v4.png')"
splashScreenScaleMode="letterbox"
title="Loading Feeds ...">
<fx:script>
< ![CDATA[
import mx.events.FlexEvent;
import mx.rpc.events.ResultEvent;
import views.HomeView;
protected function init_creationCompleteHandler(event:FlexEvent):void
{
feed.getData();
}
protected function feed_resultHandler(event:ResultEvent):void
{
loader.visible = false;
navigator.pushView(views.HomeView, event.result);
}
]]>
</fx:script>
<fx:declarations>
<feed:feed id="feed" result="feed_resultHandler(event)"/>
</fx:declarations>
<s:actioncontent>
<s:busyindicator id="loader"/>
</s:actioncontent>
</s:viewnavigatorapplication>
NOTE: En temps normal, vous ne devez pas modifier ce fichier (cette vue principale)! D’ailleurs Flash Builder ne l’ouvre pas au lancement du projet!
Le fait d’avoir mis les traitements au sein de cette vue, fait partie d’une astuce qui peremet d’alléger les traitements des autres vues. En effet, nous aurions bien pu faire ces traitements dans la vue suivante!
Expliquons le code ci-dessus.
Nous commençons par spécifier donc un espace de nom pour notre service que nous avions crée plus haut:
1
2
3
4
5 <s:viewnavigatorapplication ...
xmlns:feed="services.feed.*"
... >
...
</s:viewnavigatorapplication>
Nous ajoutons une “image de garde” ou splashcreen pour notre application:
1
2
3
4
5
6 <s:viewnavigatorapplication ...
splashScreenImage="@Embed('assets/header_v4.png')"
splashScreenScaleMode="letterbox"
... >
...
</s:viewnavigatorapplication>
Ensuite, nous définissons une action qui va se déclencher une fois que l’application aura fini de charger. Cette action va consister à faire appel au service que nous avions déclaré plus haut; et cet appel va donc charger le flux RSS depuis le serveur grâce à la méthode getData():
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 <s:viewnavigatorapplication ...
creationComplete="init_creationCompleteHandler(event)"
... >
<fx:script>
< ![CDATA[
import mx.events.FlexEvent;
...
protected function init_creationCompleteHandler(event:FlexEvent):void
{
feed.getData();
}
...
]]>
</fx:script>
...
</s:viewnavigatorapplication>
Afin de pouvoir récupérer puis traiter le résultat renvoyé par le serveur, autrement dit le contenu XML du flux RSS, nous devons déclarer un attribut result dans l’objet feed.
Ensuite, on affecte une action à cet attribut qui va s’exécuter lors de la réception du contenu XML depuis le serveur. Cette action va consister à passer à la vue suivante dont le rôle sera d’afficher la liste des articles disponibles.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 <s:viewnavigatorapplication ... >
<fx:script>
< ![CDATA[
...
import mx.rpc.events.ResultEvent;
import views.HomeView;
protected function feed_resultHandler(event:ResultEvent):void
{
loader.visible = false;
navigator.pushView(views.HomeView, event.result as);
}
]]>
</fx:script>
<fx:declarations>
<feed:feed id="feed" result="feed_resultHandler(event)"/>
</fx:declarations>
<s:actioncontent>
<s:busyindicator id="loader"/>
</s:actioncontent>
</s:viewnavigatorapplication>
Nous allons expliquer plus en détails la méthode feed_resultHandler: Flash Builder gère les vues comme une pile, on peux donc naviguer parmi ces vues en les empilant et dépilant.
Grâce à l’objet global navigator, nous allons pouvoir empiler une nouvelle vue, que nous allons appeler HomeView et en lui passera comme paramètre le résultat renvoyé par le serveur. C’est en effet, ce qu’on a fait avec cette instruction:
1
2
3 ...
navigator.pushView(views.HomeView, event.result);
...
Pour mieux comprendre la nature de la propriété result, nous allons mettre un point d’arrêt au niveau de cette ligne (celle de dessus), et examinons l’état interne de la propriété result:
Comme vous pouvez le constater, la propriété result est de type ArrayCollection où chaque entrée correspond à un article de notre blog, avec un titre, liens …
Nous allons maintenant ajouter une nouvelle vue afin de lister les articles du blog.
5 Ajout d’une nouvelle vue
Afin d’ajouter une nouvelle vue à l’application, faites un clique droit sur le répértoire views puis New > MXML Component:
Ensuite, il vous suffit de donner un nom à cette vue. Dans notre cas, nous allons l’appeler HomeView:
6 Vue: liste des articles
Maintenant que nous avons ajouté notre vue, nous allons remplacer le code généré par celui-ci:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 < ?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="Welcome To MyRSS">
<fx:script>
< ![CDATA[
import spark.events.IndexChangeEvent;
protected function list_changeHandler(event:IndexChangeEvent):void
{
navigator.pushView(views.FeedDetailsView, list.selectedItem);
}
]]>
</fx:script>
<s:list id="list" x="0" y="0" width="100%" height="100%" change="list_changeHandler(event)"
dataProvider="{data}" >
<s:itemrenderer>
<fx:component>
<s:iconitemrenderer iconWidth="32" iconHeight="32"
labelFunction="mainLabel"
messageFunction="mainMessage"
iconFunction="mainIcon"
decorator="@Embed('assets/br_next_icon_16.png')">
<fx:script>
< ![CDATA[
import mx.collections.ArrayCollection;
protected function mainLabel(item:Object):String
{
return item.title;
}
protected function mainMessage(item:Object):String
{
var cat:ArrayCollection = item.category;
return cat.source.join(',');
}
protected function mainIcon(item:Object):String
{
return 'assets/rss64x64.png';
}
]]>
</fx:script>
</s:iconitemrenderer>
</fx:component>
</s:itemrenderer>
</s:list>
</s:view>
Il y a trois choses importantes à expliquer dans ce code:
- L’ajout de la liste et du DataProvider
- L’affichage des informations grâce à l’ItemRenderer
- La gestion d’évènements pour le passage à la dernière vue: le contenue HTML de l’article sélectionné
A – Ajout de la liste et du DataProvider
Pour afficher la liste des articles disponibles sur notre blog, Nous devons ajouter un élément List à notre vue. Puis nous choisissons d’utiliser une instance de la classe DataProvider pour alimenter cette liste.
A noter, qu’il y a d’autres possibilités pour alimenter cette liste. Mais dans notre cas, nous avons choisi d’utiliser la classe DataProvider!
Nous donnons un identifiant à cette liste. On lui donne également une position absolue à 0,0 ainsi qu’une hauteur et une largeur à 100% afin qu’elle prenne tout l’espace de l’écran.
1
2
3 ...
<s:list id="list" x="0" y="0" width="100%" height="100%" dataProvider="{data}" >
...
Ensuite, nous allons alimenter cette liste grâce, donc, à l’attribut dataProvider auquel on va passer comme paramètre la valeur data qui est une variable globale à chaque vue. En, effet, chaque vue contient sa propre variable data qui contient les données passées à cette vue!
Notez bien les accolades {} qui encadrent cette variable, cela s’appelle le “data binding” en ActionScript. Il permet de récupérer la valeur d’une variable qui a été déclaré et annotée à cet effet. Vous trouverez plus d’information à ce sujet sur ici.
B – L’affichage des informations grâce à l’ItemRenderer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 ...
<s:list id="list" ... >
<s:itemrenderer>
<fx:component>
<s:iconitemrenderer iconWidth="32" iconHeight="32"
labelFunction="mainLabel"
messageFunction="mainMessage"
iconFunction="mainIcon"
decorator="@Embed('assets/br_next_icon_16.png')">
<fx:script>
< ![CDATA[
import mx.collections.ArrayCollection;
protected function mainLabel(item:Object):String
{
return item.title;
}
protected function mainMessage(item:Object):String
{
var cat:ArrayCollection = item.category;
return cat.source.join(',');
}
protected function mainIcon(item:Object):String
{
return 'assets/rss64x64.png';
}
]]>
</fx:script>
</s:iconitemrenderer>
</fx:component>
</s:itemrenderer>
</s:list>
...
Normalement, on aurait pu simplement lister les titres des articles. Cependant, nous utilisons un composant spécial nommé ItemRenderer qui va nous permettre d’afficher un peu plus d’informations que le simple titre.
Ce composant nous permet d’afficher une icone à gauche, un titre au center haut, une description des catégories au center bas et une icone de décoration à droite.
Voici donc le résultat obtenu:
Maintenant que la liste est prête, il ne nous reste plus qu’à afficher le contenu de l’article lorsqu’on appuie ou touche (puisque’on utilise notre doigt!) sur un élément de la liste.
C – Le passage à la vue suivante
Pour cela, nous allons écouter l’évènement change de la liste. Et lorsqu’un élément de cette liste a été sélectionné, on va utiliser la variable globale navigator pour empiler une nouvelle vue qui va contenir le contenu HTML de l’article choisi.
Nous procédons donc comme d’habitude, nous passons en paramètre le nom de la nouvelle vue (qu’on va créer par la suite) ainsi que l’élément sélectionné.
1
2
3
4
5
6
7
8
9
10
11
12
13 ...
<fx:script>
< ![CDATA[
import spark.events.IndexChangeEvent;
protected function list_changeHandler(event:IndexChangeEvent):void
{
navigator.pushView(views.FeedDetailsView, list.selectedItem);
}
]]>
</fx:script>
<s:list id="list" ... change="list_changeHandler(event)" ... >
...
7 vue: le contenu d’un article
Dans un premier temps, nous allons créer une nouvelle vue (voir partie 5). Nous allons la nommer FeedDetailsView. Puis nous allons remplacer le code généré par celui-ci:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 < ?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"
xmlns:mx="library://ns.adobe.com/flex/mx"
title="{data.title}"
creationComplete="init(event)">
<fx:script>
< ![CDATA[
import mx.events.FlexEvent;
import spark.events.ViewNavigatorEvent;
protected function button1_clickHandler(event:MouseEvent):void
{
navigator.popView();
}
protected var webView:StageWebView = new StageWebView();
public function init(event:FlexEvent):void
{
webView.stage = this.stage;
webView.viewPort = new Rectangle( 0, 70, stage.stageWidth, stage.stageHeight);
webView.loadURL(data.link);
addEventListener(ViewNavigatorEvent.REMOVING,
function(event:ViewNavigatorEvent):void{
webView.dispose();
}
);
this.stage.addEventListener(StageOrientationEvent.ORIENTATION_CHANGE,
function(event:StageOrientationEvent):void{
webView.viewPort =
new Rectangle( 0, 70, stage.stageWidth, stage.stageHeight);
}
);
}
]]>
</fx:script>
<s:navigationcontent>
<s:button label="Back" click="button1_clickHandler(event)">
<s:icon>
<s:multidpibitmapsource source320dpi="@Embed('assets/br_prev_icon_24.png')"/>
</s:icon>
</s:button>
</s:navigationcontent>
</s:view>
Ce code contient des nouvelles choses qu’on donc s’empresser d’expliquer:
Dans un premier temps, nous avons besoin d’afficher du contenu HTML, celui de l’article. Nous allons donc utiliser l’URL de cet article afin de charger le contenu HTML depuis le blog.
Pour cela, nous devons instancier la classe StageView() qui est capable d’afficher du contenu HTML. Après l’avoir configuré correctement, nous allons chargé la page HTML grâce à l’instruction:
1
2
3 ...
webView.loadURL(data.link);
...
NOTE: Nous allons supposer que votre serveur est bien configuré pour fournir du contenus optimisés pour les appareils mobiles!
Rappelez-vous de la variable data expliquée plus haut!!!
Ensuite, afin d’optimiser au minimum la mémoire de l’appareil mobile, nous devons libérer l’instance de la classe StageView créée précédemment. Pour cela, nous allons observé l’évènement ViewNavigatorEvent.REMOVING qui sera déclenché lorsqu’on va changer de vue. Et dès que cet évènement est déclenché, nous supprimons l’instance webView:
1
2
3
4
5
6
7 ...
addEventListener(ViewNavigatorEvent.REMOVING,
function(event:ViewNavigatorEvent):void {
webView.dispose();
}
);
...
NOTE 1: Veuillez noter que nous observons l’évènement ViewNavigatorEvent.REMOVING sur le this, qui est donc implicite, et qui désigne la vue courante!
NOTE 2: Veuillez noter que nous avons utilisé ici, une fonction anonyme en tant que second paramètre pour addEventListener et ceci pour plus de simplicité (on aurait bien pu la déclarer ailleurs)!
Une autre “optimisation” que nous pouvons ajouter, consiste à adapter la zone d’affichage du contenu HTML aux dimensions de l’appareil mobile lorsque ce dernier change de mode d’orientation. Pour cela, nous ajoutons ces quelques lignes de code:
1
2
3
4
5
6
7 ...
this.stage.addEventListener(StageOrientationEvent.ORIENTATION_CHANGE,
function(event:StageOrientationEvent):void{
webView.viewPort = new Rectangle( 0, 70, stage.stageWidth, stage.stageHeight);
}
);
...
NOTE: nous observons l’évènement StageOrientationEvent.ORIENTATION_CHANGE sur l’instance stage de la vue courante!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 <s:view ...
creationComplete="init(event)">
...
<fx:script>
< ![CDATA[
import mx.events.FlexEvent;
import spark.events.ViewNavigatorEvent;
...
protected var webView:StageWebView = new StageWebView();
public function init(event:FlexEvent):void
{
webView.stage = this.stage;
webView.viewPort = new Rectangle( 0, 70, stage.stageWidth, stage.stageHeight);
webView.loadURL(data.link);
addEventListener(ViewNavigatorEvent.REMOVING,
function(event:ViewNavigatorEvent):void{
webView.dispose();
}
);
this.stage.addEventListener(StageOrientationEvent.ORIENTATION_CHANGE,
function(event:StageOrientationEvent):void{
webView.viewPort =
new Rectangle( 0, 70, stage.stageWidth, stage.stageHeight);
}
);
}
...
]]>
</fx:script>
...
Voici donc la vue que nous venons de coder:
Et pour finir, nous ajoutons un bouton retour vers la vue précédente, celle qui liste les articles.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 <fx:script>
< 



























