Flex – Bonnes pratiques applications mobile

On ne le répètera jamais assez, l’expérience utilisateur est l’une des choses les plus importantes dans les environnements riches. Ainsi, cette page regroupe les bonnes pratiques et astuces de développement d’applications pour téléphones et tablettes qui vont nous permettre de nous y aider.

Fermer l’application correctement

Sur android, le fait d’appuyer sur le bouton retour ne va pas fermer l’application. Celle-ci passera en arrière plan. Si vous n’avez pas besoin que l’application reste en arrière plan, il est possible de capturer l’évènement de cette touche et de lancer une méthode pour quitter l’application.

protected function init() { // fonction lancée sur l'évènement applicationComplete
  NativeApplication.nativeApplication.addEventListener(KeyboardEvent.KEY_DOWN,onKeyDown); // on écoute le clavier sur nativeApplication
}
private function onKeyDown(evt:KeyboardEvent):void {
  switch (evt.keyCode) {
    case Keyboard.BACK :
      NativeApplication.nativeApplication.exit();
      break;
  }
}

Il est également possible de bloquer l’utilisation de la touche, comme toute touche du téléphone, en insérant la méthode suivante après le case :

evt.preventDefault();

Utilisation de includeIn

Il est possible d’adapter, avec un minimum de code, des Views par rapport à la situation de l’environnement. A savoir si nous avons affaire à un grand écran de tablette, ou si nous sommes en mode portrait ou landscape (paysage). En mettant includeIn, notre View va se sélectionner suivant la valeur mise dans currentState. Nous modifions cette valeur à chaque changement d’état de l’environnement, à savoir l’évènement ResizeEvent :

private function resizeHandler(evt:ResizeEvent):void {
  var isPortrait:Boolean = height > width;
  var isTablet:Boolean = height > 690 || width >960;
  currentState = (isPortrait ? "portrait":"paysage") + (isTablet ? "Tablet":"Phone");
}
<SplitViewNavigator includeIn="portraitTablet, paysageTablet" >...</SplitViewNavigator>
<ViewNavigator includeIn="portraitPhone, paysagePhone" />

ou encore regrouper les états par type, si finalement nous n’avons seulement besoin de connaître qu’une partie des informations :

<states>
  <State name="portraitPhone" stateGroups="portrait, phone" />
  <State name="paysagePhone" stateGroups="paysage, phone" />
</states>
<SplitViewNavigator includeIn="tablet" >...</SplitViewNavigator>
<ViewNavigator includeIn="phone" />

Scale automatique pour différents DPI

Afin d’adapter notre application suivant le support, il est possible de définir notre DPI de base :

<Application applicationDPI="160">
  <Button width="150" height="40" />
</Application>

Aussi, il est possible de définir des images ou icones directement grâce à la balise MultiDPIBitmapSource :

<Button click="refresh()">
  <icon>
    <MultiDPIBitmapSource
      source160dpi="@Embed('iconDB160.png')"
      source240dpi="@Embed('iconDB240.png')"
      source320dpi="@Embed('iconDB320.png')" />
  </icon>
</Button>

Afficher un bouton suivant l’OS utilisé

Les supports IPhone et IPad, au contraire des supports Androids, ne possèdent pas de bouton « retour ». Ils doivent donc être intégrés directement dans l’application. Il est possible de faire en sorte que ce bouton soit visible seulement sur IOS et non sur Android en effectuant l’astuce suivante :

<Button visible= »{DeviceInfo.isIOS} » />

De la même façon, on peut modifier les états et afficher seulement ce qui nous intéresse suivant le besoin :

<states>
  <State name="iosPhone" />
  <State name="..." />
</states>
<navigationContent.iosPhone>
  <Button click="actionClick()" />
</navigationContent.iosPhone>

Le splashscreen sur iOS

Même s’il est possible de définir directement dans Flash builder le fichier du splashscreen à utiliser, il existe une légère différence sur iOS. En effet, il est nécessaire d’utiliser des fichiers avec des noms bien définis. Cela va permettre d’avoir un splashscreen qui apparaît de suite au lieu d’avoir un temps d’attente noir. Ceux-ci sont :

  • Default.png
  • Default-Lanscape.png (optionnel)
  • Default-Portrait.png (optionnel)

Ils doivent être intégrés à la racine de l’application et peuvent être déployés en les sélectionnant dans les propriétés du projet, paquet de génération Flex / Apple iOS, onglet Contenu. Cela permet d’éviter de les déployer sur Android où ils ne serviront qu’à prendre de la place inutilement.

Augmenter les performances

Les évènements

Bien évidemment, il faudra limiter autant que possible tous les évènements lourds tel que ENTER_FRAME et l’utilisation des classes à Timer.

Afin d’éviter de « freezer » les transitions entre les vues, il est préférable d’utiliser l’évènement viewActivate plutôt que creationComplete.

Le nombre est important

Il faut savoir également que le nombre d’objets affichés sur l’écran a une réelle importance quant à l’utilisation du processeur. Certains forums préconisent de mettre le buttonMode à false sur chaque objet qui ne nécessite pas d’interaction avec l’utilisateur mais les différents tests que j’ai pu effectués n’ont pas vraiment été concluant.

De plus, lorsque l’on met des objets dans des objets, si un seul objet « enfant » change, l’ensemble des objets de l’objet parent doivent être redessinés. Donc il faut éviter d’intégrer trop d’objets dans un seul si chacun doit être manipulé individuellement.

Les filtres sont un gouffre à CPU, utilisez des images pré-filtrés plutôt que d’assigner des filtres sur des images.

Les images

Le format d’image a son importance. Les images png sont les plus rapides à décoder que les images jpg ou gif.

Le mode debug

Cela peut paraître ridicule, mais n’oubliez pas de déployer la version release (finale) et non la version debug (test). Si une version release est très peu consommatrice en CPU en idle (entre 0% et 1%), ce n’est pas le cas de la version debug qui est très souvent au delà de 5% même en idle à cause des échanges nécessaires en AIR et Flash Builder.

Utiliser et typer les tableaux

Il est plus rapide de définir la longueur d’un tableau que d’en recréer un. A savoir :

var t:Array = new Array();
// on remplit le tableau
t.length = 0;  // équivalent à t = new Array() mais plus rapide.

Le tableau que l’on reconnait par le mot clé Array peut être remplacé par Vector. Il s’agit simplement d’un Array qui est typé, c’est-à-dire qui ne peut contenir qu’un certain type d’élément. Par exemple :

var v:Vector.<String> = new Vector.<String>(); // définit un tableau de texte
var v:Vector.<MovieClip> = new Vector.<MovieClip>();  // définit un tableau de clip

Le Vector peut être de longueur fixe (plus rapide), doit comporter une valeur (même null) à chacun de ses index, contrairement à un Array.

Si vous n’avez pas besoin de trier ou filtrer une liste, utilisez ArrayList au lieu de ArrayCollection, qui est beaucoup plus rapide.

Effacer des données

La fonction delete est gourmande en ressource. Il vaut mieux définir une variable à null plutôt que d’essayer de l’effacer grâce à delete.

cacheAsBitmap

L’utilisation de cacheAsBitmap permet de redessiner plutôt que d’effectuer un nouveau rendu. Cela est utile lorsque l’on utilise des listes avec scrolling. Toutefois, il faut l’utiliser à bon escient. Mettre un cacheAsBitmap=true sur une image ne va pas l’accélérer, au contraire, puisque chaque petite modification de cette image va le remettre en cache, car elle doit être regénéré. Faîtes le surtout sur les zones de textes qui ont des rendus assez long en terme de process.

Tous les objets enfants sont inclus dans le cache, il n’est donc pas nécessaire de définir le cacheAsBitmap d’un objet dont un parent a déjà cette option. Au contraire, cela va créer une double mise en cache inutile.

opaqueBackground

En mettant cette option, AIR va interpréter le fond de l’objet comme étant opaque, c’est-à-dire qu’il n’y a pas de gestion de la transparence. De ce fait, les calculs faits sur ces objets seront beaucoup plus rapides.

Afin de l’utiliser, il suffit d’attribuer une couleur à opaqueBackground.

Aller plus loin

La plupart des ces astuces et bonnes pratiques peuvent être retrouvées sur Adobe TV.

Laisser un commentaire

 

*