AIR Mobile – Application Pokémon (22) – Splash Screen pour différentes tailles et orientations
Une des premières choses que va voir votre utilisateur, c'est le splash screen de votre application. Pour les francophiles les plus résistants, un splash screen est une image qui apparaît pendant que l'application est chargée en mémoire par le téléphone. Cette image peut être affichée plusieurs secondes, il est donc important de faire bonne impression.
La propriété splashScreenImage du SDK Flex
Le SDK Flex comprend une propriété "splashScreenImage" qui va agir sur le preloader de votre application. Celle ci vous permet de donner une image qui sera affichée comme Splash Screen. Vous avez aussi une autre propriété qui vous permet d'indiquer si vous voulez étirer l'image ou la laisser à une taille toujours identique.
Toutes les informations sur ces propriétés sont résumées dans ce billet de flex-tutorial.fr:
AIR Mobile – Ajouter un Splash Screen avec splashScreenImage
Une notion de splash screen différente selon les plate-formes
Suivant la plate-forme, le splash screen ne représente pas le même instant de chargement de l'application. Ainsi, si vous ajoutez seulement un splash screen avec la propriété splashScreenImage, votre iDevice affichera d'abord du blanc, puis votre splashScreen puis votre application. Idem pour BlackBerry Playbook. Pour Android en revanche, c'est la splashScreenImage qui prime et qui sera affiché tout du long.
Alors pourquoi ce n'est pas la même chose sur iOS? Et bien les applications natives utilisent un système différent pour définir des splash screen. Ceux-ci ne sont pas une propriété du programme mais des images présentes simplement dans les sources du projet, portant un certain nom. Ce nom doit suivre exactement certaines conventions définies par Apple pour être prise en compte sans votre intervention.
Les conventions Apple pour iOS
Avant de faire un minimum de hacking, on va voir quelles sont les conventions définies par Apple pour ses Splash screen. Vous devrez utiliser des images avec des noms bien spécifiques, aux dimensions exactes pour qu'elle calent parfaitement sur l'appareil.
Ces images devront être placées directement dans le dossier "src" de votre application, le compilateur se chargera de les prendre en compte. Attention à bien respecter la casse !
Voici les références sur le site d'Apple:
http://developer.apple.com/library/ios/#qa/qa1588/_index.html
Build-Time Configuration Details
Voici donc les images et leur formats:
- Default.png : 320 x 480 pour iPhone non retina (3GS). Portrait uniquement
- Default@2x.png : 640 x 960 pour iPhone 4 (retina). Portrait uniquement
- Default-Portrait.png : 768 x 1004. iPad portrait
- Default-Landscape.png : 1024 x 748. iPad paysage
- Default-LandscapeRight.png : 1024 x 748. iPad right-oriented landscape
- Default-LandscapeLeft.png : 1024 x 748. iPad left-oriented landscape
- Default-PortraitUpsideDown.png : 768 x 1004. iPad upside-down portrait
A noter que si votre application est définie comme une application plein écran dans le *-app.xml, il faut rajouter la taille de la barre de notification. 1024 x 748 devient 1024 x 768 et 768 x 1004 devient 768 x1024
Une image, c'est tout?
Si vous avez lu l'article sur le splash screen vous avez vu que je termine sur une note un peu négative, sur le fait que l'on ne puisse donner qu'une seule image. On aurait donc jamais un rendu impeccable suivant les tailles d'écran et les orientations. Le mode "stretch" ne vous sortira pas d'affaire puisque vous le savez, peu importe les conditions, on voit toujours quand un image est étirée (en + ou en -).
Je donne une piste à la fin du billet pour expliquer comment vous pouvez faire pour contourner ce problème : surcharger le preloader par défaut.
Créer son propre Preloader : prendre de l'inspiration
On va maintenant voir comment créer son propre preloader. Si vous suivez flex-tutorial, vous connaissez le conseil que je donne à chaque fois: Flex est Open Source, n'hésitez pas à aller voir dans le code, sûrement la meilleure référence.
Si vous pensez que je trouve parfois des solutions magiques, ce n'est pas le cas, je vais expliquer exactement le raisonnement (parfois faux, il faut savoir se planter) que j'ai eu pour arriver à une situation potable.
Allons donc voir dans Application.as, on doit sûrement parler de splashScreen puisque la propriété "splashScreenImage" provient de cette classe (Ctrl+click sur une propriété pour voir où elle est définie).
C'est parti, Ctrl+T, Application.as dans le package spark.component. Allons voir la documentation de "splashScreenImage", on repère un renvoi qui peut nous intéresser:
* @see spark.preloaders.SplashScreen
Ok, donc il y a une classe qui se nomme SplashScreen et la documentation nous indique que c'est celle utilisée pour le thème mobile:
* The spark.preloaders.SplashScreen class is the default preloader for Mobile Flex applications. * This property cannot be set by ActionScript code; it must be set in MXML code.</p>
On est reparti dans le SDK, Ctrl+T, SplashScreen.as. On regarde un peu ce qui est définit dans la classe, une méthode initialize() par exemple qui est l'implémentation de l'interface IPreloaderDisplay.
C'est en fait le "kick" qui est envoyé par Flex de base et indique au preloader qu'il doit se lancer. La classe de Preloader est assez spéciale dans Flex car son exécution se passe avant le chargement complet de l'application. Vous avez donc un accès limité à de nombreuses informations. En effet, le Preloader est chargé avant toute l'application, il doit donc avoir un nombre très limité de dépendances vers le framework. Ici, vous avez principalement accès au "stage" et c'est tout ce dont vous aurez besoin.
On continue, un peu plus bas, une méthode intéressante:
mx_internal function getImageClass(dpi:Number, aspectRatio:String):Class{ if ("splashScreenImage" in info) return info["splashScreenImage"]; return null; }
Cette méthode renvoie une Class, c'est est fait l'image qui sera instanciée pour servir de splash screen. Vous verrez souvent dans le preloader, un accès à "info" qui est en fait "sysManager.info()". Cet objet contient les informations qui vous sont nécessaires à ce moment de l'initialisation de l'application.
On continue avec la méthode suivante qui va être la dernière méthode intéressante de cette classe : prepareSplashScreen(). Trois lignes qui vont nous intéresser:
// Capture device dpi and orientation
var dpi:Number = dpiProvider.runtimeDPI;
var aspectRatio:String = (stage.stageWidth < stage.stageHeight) ? StageAspectRatio.PORTRAIT : StageAspectRatio.LANDSCAPE;
var imageClass:Class = getImageClass(dpi, aspectRatio);
La suite instancie la Class renvoyée par imageClass et l'ajoute au stage. Voilà, on a assez d'informations pour créer notre propre preloader. En fait, tout le code est générique, sauf cette méthode getImageClass qui ne fait "que" renvoyer ce que l'on a précisé en splashScreenImage. Pourtant on lui passe les DPI est l'aspectRatio et on en fait rien. A mon avis, les dev du SDK avaient déjà prévu que l'on puisse surcharger cette classe et avait laissé ce point d'accès pour plus tard
.
Création de son preloader
On va utiliser l'héritage pour surcharger cette méthode getImageClass et faire notre travail là-dedans. Créez donc le package de votre choix et créer une New > ActionScriptClass avec comme Base, la classe SplashScreen. Nommez-là MultiSplashScreen par exemple.
Avant de partir le nez dans le code, regardons la situation. On a une méthode getImageClass qui va renvoyer une valeur de type Class. Cette valeur va dépendre du périphérique sur lequel tourne l'application. On pourra utiliser la valeur de DPI et celle d'aspectRatio qui est passé à getImageClass mais on pourra aussi utiliser Capabilities pour déterminer le device utilisé.
Alors pour retourner une Class, comment fait-on d'habitude? On utilise une directive Embed. Par exemple, pour déterminer notre splashScreenImage, on utilise:
splashScreenImage="@Embed(source='/assets/...png')"
Du coup, ce que l'on pourrait faire, c'est définir plusieurs variables, pour iPhone, iPad, iPad landscape, … On remplit chacune de ces variables avec un Embed pointant vers un image différente et on choisit ce que l'on va renvoyer dans getImageClass(). Facile, non?
Mais le mieux, ce ne serait pas plutôt de faire une classe réutilisable pour d'autre projets? Si on met les directives Embed dans MultiSplashScreen, plus moyen de les modifier de l'extérieur. Pas top.
Ma première idée fut de définir ces Embed dans le MXML principal, comme on le fait pour splashScreenImage comme ceci:
<?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.PokemonInfosHomeView"
initialize="onInit(event)"
xmlns:fnicollet="com.fnicollet.*">
<s:preloader>
<fnicollet:MultiSplashScreen androidSmartphone="@Embed(source='/assets/splashscreen-export.png')" />
</s:preloader>
...
Parfait, ça compile, c'est une solution qui permet de réutiliser facilement cette classe. Testez en debug et vous allez avoir une mauvaise surprise. Le constructeur n'est appelé qu'après le pré-chargement de notre application.
Donc notre système ne sert à rien. Pourquoi? Et bien en fait, c'était indiqué dans la documentation que l'on a vu au début:
* The spark.preloaders.SplashScreen class is the default preloader for Mobile Flex applications.
* This property cannot be set by ActionScript code; it must be set in MXML code.</p>
Définir notre MultiSplashScreen comme on l'a fait dans le MXML revient au final à le définir en AS (rappelez vous, le MXML est compilé en AS au moment de la compil). Tout ce que l'on peut faire, c'est ceci:
<?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.PokemonInfosHomeView"
initialize="onInit(event)"
xmlns:fnicollet="com.fnicollet.*"
preloader="com.fnicollet.MultiSplashScreen">
...
On donne ici juste une référence à la classe MultiSplashScreen et on n'essaie pas de créer une instance, c'est Flex qui va s'en charger "under the hood". Tant pis donc pour notre idée de preloader ré-uilisable. On va revenir au preloader "en dur", ce sera suffisant
.
Code du preloader final
Bon ce tutorial commence à s'allonger, et maintenant, il ne nous reste plus qu'à écrire notre méthode getImageClass. Je vais arrêter de faire durer, donc voici le code que j'ai écrit:
package com.fnicollet
{
import flash.display.StageAspectRatio;
import flash.system.Capabilities;
import mx.core.mx_internal;
import spark.preloaders.SplashScreen;
use namespace mx_internal;
public class MultiSplashScreen extends SplashScreen{
[Embed(source="Default.png")]
public var iPhone:Class;
[Embed(source="Default@2x.png")]
public var iPhone4:Class;
[Embed(source="Default-Portrait.png")]
public var iPadPortrait:Class;
[Embed(source="Default-Landscape.png")]
public var iPadLandscape:Class;
[Embed(source="Default-Android.png")]
public var androidSmartphone:Class;
[Embed(source="Default-Android-Landscape.png")]
public var androidSmartphoneLandscape:Class;
[Embed(source="Default-Android-Tablet.png")]
public var androidTablet:Class;
[Embed(source="Default-Android-Tablet-Landscape.png")]
public var androidTabletLandscape:Class;
public function MultiSplashScreen(){
super();
}
override mx_internal function getImageClass(dpi:Number, aspectRatio:String):Class
{
/*
trace("manufacturer: " + Capabilities.manufacturer);
trace("os: " + Capabilities.os);
trace("screenDPI: " + Capabilities.screenDPI);
trace("screenResolutionX: " + Capabilities.screenResolutionX);
trace("screenResolutionY: " + Capabilities.screenResolutionY);
*/
var w:int = Math.max( Capabilities.screenResolutionX, Capabilities.screenResolutionY );
var h:int = Math.min( Capabilities.screenResolutionX, Capabilities.screenResolutionY );
var manuf:String = Capabilities.manufacturer.toLowerCase();
if (manuf.indexOf("ios") != -1){
// StageAspectRatio.PORTRAIT : StageAspectRatio.LANDSCAPE
if (w <= 480){
return iPhone;
} else if (w == 640){
return iPhone4;
}else if (w > 640){
return aspectRatio == StageAspectRatio.PORTRAIT ? iPadPortrait : iPadLandscape;
}
} else if (manuf.indexOf("android") != -1){
if (h <= 480){
return aspectRatio == StageAspectRatio.PORTRAIT ? androidSmartphone : androidSmartphoneLandscape;
} else {
return aspectRatio == StageAspectRatio.PORTRAIT ? androidTablet : androidTabletLandscape;
}
} else if (manuf.indexOf("vmware") != -1){
// VMWare = BlackBerry Simulator
}
return super.getImageClass(dpi, aspectRatio);
}
}
}
Comme vous le voyez, les Embed sont définis en dur, il faut donc que vous ayez les assets au bon endroit dans votre structure. Pour les SplashScreen iOS, c'était assez simple puisqu'il suffisant de pointer vers Default.png, … qui sont les convention Apple. On sait que ces splashscreen ne seront pas autre part.
Plus difficile pour Android. Il y a de nombreux appareils, la plupart ayant une taille d'écran unique. Il y a plusieurs modèles ayant une résolution de 480×800 dans la liste:
J'ai donc pris cette taille pour le "androidSmartphone" pour les tablettes, c'est plus difficile, car pour les 2 tablettes de la liste, on a des dimensions différentes. Arbitrairement, j'ai choisis pour "androidTablet", le format du Galaxy Tab (car on fait nos démos dessus).
Bien sûr, cette classe peut être modifiée pour vos besoin spécifiques. Attention tout de même à ne pas embarquer trop d'images, sinon c'est le poids de votre application qui va en prendre un coup
.
Il y a encore quelque TODO, notamment pour le Playbook sur lequel je n'ai pas eu le loisir de tester. Si vous souhaitez améliorer cette classe, n'hésitez pas à utiliser les commentaires !
Flex 4 – Différences entre Flex 3 et Flex 4 (1-Migration vers Flex 4)
Traduction de l'article Differences between Flex 3 and Flex 4 beta de Joan Lafferty.
Flex 4 (nom de code Gumbo) représente un changement majeur par rapport à Flex 3. Flex 4 introduit en effet une nouvelle architecture de composants et de skinning. En tant que développeur Flex 3, vous ne devriez cependant par rencontrer trop de difficultés pour compiler vos applications Flex 3 avec le SDK Flex 4, car la compatibilité descendante (backwards compatibility) avec Flex 3 est conservée.
Dans cette série d'articles, je vais donner d'ensemble des objectifs principaux de Flex 4, des différences d'architecture ainsi qu'une introduction aux changements de composants, de mise en pages mais aussi l'utilisation des States et des effets. On verra aussi comment compiler votre application Flex 3 avec le SDK Flex 4. Cet article ne couvre pas les nouvelles fonctionnalités de Flex 4.
Tout au long de cette série d'articles, le terme de "Composants Halo" réfère aux composants inclus dans Flex 3. Le terme "Composants Spark" réfère au nouvel ensemble de composant de Flex 4.
Migration d'applications vers Flex 4
Pour une migration d'application Flex 3 vers Flex 4, vous n'aurez que peu de choses à modifier. A part quelques résolutions de bugs et un changement du thème par défaut, attendez-vous à ce que votre application fonctionne de la même manière (voire mieux) qu'elle le faisait en Flex 3. Cependant, voici quelques points sur lequels vous devriez être attentifs.
Dépendance à Flash Player 10
Compilez bien pour Flash Player 10. Flex 4 impose la compilation en FP10.
Les Type Selector (style) ont besoin d'un namespace
Un type selector CSS lie une classe Flex à un style. Par exemple, les sélecteurs CSS suivant agissent sur Button et DateField:
Button {
cornerRadius: 10;
}
DateField {
color: #780800;
}
Dans Flex 4, quand une application utilise des Type Selector, un namespace est requis. Si vous utilisez uniquement le namespace MXML 2006 dans votre application Flex, ajouter la déclaration de namespace par défaut à votre CSS:
<mx:Style> @namespace "http://www.adobe.com/2006/mxml"; … </mx:Style>
Si vous utilisez plusieurs namespaces dans votre application, vous devrez les indiquer dans votre CSS (voir plus loin).
De plus, si votre application utilise une méthode comme StyleManager.getStyleDeclaration("Button"), le type selector devra inclure le package. Par exemple, l'appel à getStyleDeclaration() deviendrait StyleManager.getStyleDeclaration("mx.controls.Button")
Changement de theme
Le theme par défaut des composant Flex 3 (Halo) est maintenant le theme Spark. C'est pourquoi le look et les tailles de votre application pourraient être différent en utilisant Flex 3. Si vous voulez tout de même utiliser le look Flex 3, vous le pouvez car Flex 4 inclut toujours le thème Halo de Flex 3. Pour compiler en utilisant le thème Halo, utilise le flag -compatibility-version=3.0 dans les arguments du compilateur ou bien le flag -theme. Pour cela, dans Flash Builder 4, vous devez modifier le paramètre Additional Compiler Arguments dans la section Flex Compiler du panel de propriétés. Si vous utilisez ces arguments, assurez vous que le répertoire framework/themes/Halo est bien dans votre Source Path.






