Les technologies mobiles reposent sur un ensemble de contraintes dont une des principales est probablement le manque d’espace d’affichage. Personnellement, je dois avouer que je prend cette contrainte plutôt comme un avantage car cela me rappelle énormément mes débuts en programmation. A l’époque, je codais en Casio-Basic sur Casio avant d’acquérir une Texas Instrument Voyage 200 et de découvrir le Ti-Basic et surtout le C (aie des pointeurs ^^). L’environnement général me plaisait énormément car je trouvais l’ensemble très puissant (13Mhz) et restreint (pas de threads, pas de multi-processus, etc.). L’écran de cette calculatrice était de taille réduite et monochrome. Remplir cet “amat de pixel” était donc une tâche largement plus simple pour le non-designer que je suis que de réaliser une application PC ou un site web. L’écran n’était malheureusement pas tactile. C’est uniquement lorsque j’ai commencé à coder sur iPhone et Android que j’ai découvert ces possibilités …

Les interfaces mobiles se basent donc sur des concepts ergonomiques et tactiles qui sont absents des plateformes classiques : les gestes (ou gestures). Pour surmonter ces problèmes de tailles de terminal réduit, de nombreux mouvements sont apparus. Les principaux sont donnés dans la liste ci-dessous :

  • Simple click : Consiste à appuyer sur l’écran et à relacher sans avoir bouger son doigt.
  • Double click : Obtenu en effectuant deux clicks au même endroit à la suite. Le temps entre les deux clicks doit également être assez bref.
  • Scroll : Action d’appuyer à l’écran et de déplacer son doigt sans relâcher la pression.
  • Fling : Obtenu lorsqu’on appuie à l’écran, qu’on effectue un mouvement brusque et qu’on relâche rapidement l’écran
  • Long click : Généré en pressant l’écran de façon prolongée et sans bouger

Créer une interface graphique sur Android revient souvent à utiliser l’ensemble de ces gestes. MetroMap utilise par exemple plusieurs d’entre-eux (le simple click, le scroll, et le fling). Lors de la conception de cette application, j’ai décidé d’effectuer manuellement la détection de ces mouvements en utilisant les valeur “standards” d’Android données dans ViewConfiguration. Il existe pourtant une classe qui facilite grandement la reconnaissance de ces gestures : la classe GestureDetector. Son utilisation est on-ne-peut-plus-simple :

package com.cyrilmottier.android.gesturedetector;
 
import android.content.Context;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.GestureDetector.OnGestureListener;
 
public class MyView extends View implements OnGestureListener {
 
    private GestureDetector mGestureDetector;
 
    public MyView(Context context) {
        super(context);
        mGestureDetector = new GestureDetector(this);
    }
 
    public boolean onTouchEvent(MotionEvent event) {
        return mGestureDetector.onTouchEvent(event);
    }
 
    public boolean onDown(MotionEvent arg0) {
        return false;
    }
 
    public boolean onFling(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) {
        return false;
    }
 
    public void onLongPress(MotionEvent arg0) {
    }
 
    public boolean onScroll(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) {
        // You can do here whatever you want to handle scrolling
        return true;
    }
 
    public void onShowPress(MotionEvent arg0) {
    }
 
    public boolean onSingleTapUp(MotionEvent arg0) {
        return false;
    }
 
}

Implémenter l’interface OnGestureListener oblige le développeur à définir l’intégralité des méthodes (notion de classe abstraite pure). C’est un “problème” inhérent au langage qui n’autorise tout simplement pas les méthode optionnelles d’interface (contrairement à l’Objective-C par exemple) et qui peut être contourné en utilisant une classe qui pré-implémente l’intégralité des méthodes.

Note : Ceux qui s’intéresse à la raison de l’abscence des méthode d’interface optionnelle comprendront que la notion d’interface Java est tout simplement vu comme un contrat qui DOIT obligatoirement être respecté. L’Objective-C quant à lui autorise les méthodes d’interface (ou plus précisément de protocole) optionnelles car c’est un langage qui effectue les vérifications au runtime et non à la compilation comme le fait Java.

Android fournit une classe permettant d’effectuer cette manipulation. Si notre code consiste à seulement gérer le scroll, on aura seulement :

package com.cyrilmottier.android.gesturedetector;
 
import android.content.Context;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
 
public class MyView extends View {
 
    private GestureDetector mGestureDetector;
 
    private class ScrollHandler extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            // You can do here whatever you want to handle scrolling
            return true;
        }
    }
 
    private final ScrollHandler mScrollHandler = new ScrollHandler();
 
    public MyView(Context context) {
        super(context);
        mGestureDetector = new GestureDetector(mScrollHandler);
    }
 
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return mGestureDetector.onTouchEvent(event);
    }
 
}

Malgré l’impressionante facilité, la classe GestureDetector ne permet malheureusement pas de détecter l’intégralité des mouvements. On regrette, par exemple, la présence de callbacks sur des mouvements “multi-touch” : le pinch, le rotate, etc. Je suis certain qu’il y a de bonne raison pour cette absence et je ne pourrais donc que motiver les plus courageux à coder leur propre MultitouchGestureDetector. N’hésitez surtout pas à m’informer dans un commentaire ou par courriel si vous développez votre propre librairie. Happy coding!

Voilà un bon moment que je n’ai pas rédigé d’article sur ce blog. La raison est assez simple et n’est absolument pas dûe à un manque de créativité (loin de là … là dessus je n’ai pour l’instant aucun problème : les idées me sautent sans arrêt à l’esprit ^^) ou tout simplement la feignantise … non le problème est beaucoup plus sérieux puisqu’il s’agit d’un simple mais non moins terrible manque de temps ! Beaucoup auront compris pourquoi : je veux faire trop de choses mais je n’arrive pas à me dédoubler ou mieux me n-doubler !

Après un article plus que complet concernant le tramage sous Android, je souhaitais repartir sur un sujet plus classique et plus succinct traitant d’optimisation. Lorsqu’on apprend le Java, on est rarement confronté aux problèmes de performances puisqu’on apprend, en général, sur un ordinateur. Malheureusement lorsqu’on applique purement et simplement ces enseignements dans le monde du mobile et notamment sur Android, on se retrouve souvent confronté à des problèmes de performances. Le fameux GC n’est pas loin …

L’exemple le plus flagrant est celui de l’utilisation des objets de type “dictionnaire” qui enregistrent les valeurs sous la forme de paires (clés, valeurs). Cette classe, souvent extrêmement utile et irremplacable porte le nom de HashMap<K, V>. L’utilisation d’une telle classe implique malheureusement d’utiliser des objets comme clé ET comme valeur. Cela signifie que si vous souhaitez avoir une HashMap<int, String> vous aurez en fin de compte une HashMap<Integer, String> dans laquelle le type Integer est un objet qui contient un unique int (méthode du boxing).

Etant multi-compétence, Android et iPhone, je rencontre souvent des gens qui découvrent le monde Android et n’hésite pas à faire des HashMap un peu partout en lieu et place des traditionnels NSDictionnary. Il s’avère que la programmation sous iPhone est très différente puisque le choix de la classe à utiliser est très restreint : NSArray pour un tableau, NSDictionnary pour un dictionnaire, etc. En réalité, développer sous iPhoneOS avec des NSDictionnary ne pose pas autant de problème puisqu’il n’y a pas de notion de GC (la ressource est libérée instantanément). En conséquence le boxing/unboxing ne coûte relativement rien.

Prenons MetroMap à titre d’exemple : cette application contient, vous vous en douter, un cache de Bitmaps. Ce dernier associe simplement des Bitmap (valeurs) à des int (clé représentant l’identifiant du Drawable - R.drawable.XXX - utilisé pour la tuile). Pour éviter la création inutile d’objets de type Integer j’ai donc intelligemment (bah oui je fais pas des trucs débiles !) préféré utiliser la classe SparseArray :

package com.cyrilmottier.android.metromap.adapter;
 
import java.lang.ref.SoftReference;
 
import android.util.SparseArray;
 
public class EfficientCache<T> implements Cache<T> {
 
    private SparseArray<SoftReference<T>> mCache;
 
    public EfficientCache(int cacheSize) {
        mCache = new SparseArray<SoftReference<T>>(cacheSize);
    }
 
    public T get(int key) {
        final SoftReference<T> softRef = mCache.get(key);
        return (softRef == null) ? null : softRef.get();
    }
 
    public void put(int key, T element) {
        mCache.put(key, new SoftReference<T>(element));
    }
 
}

Cette classe vous permet d’éviter le surcoût de création d’objets inutiles (les clés) à moindre “frais”. Lorsque cela est possible (c’est à dire lorsque vos clés sont des int, je vous conseille vivement de préférer cette classe à la basique et traditionnelle HashMap<K, V>

Le framework Android fournit dans android.util, un ensemble de 3 classes qui peuvent être utilisées en lieu et place d’HashMap<K, V>:

  • SparseArray permet de mapper des objets avec des int. Cela revient donc à un “vrai” HashMap<int, V>
  • SparseIntArray : à l’instar de SparseArray, cette classe fait correspondre des int avec des int. C’est donc similaire (en terme de fonctionnalités) à HashMap<int, int>
  • SparseBooleanArray : Revient à HashMap<int, boolean>

A votre tour d’optimiser vos programmes et de supprimer les HashMaps inutiles.

Comme vous l’avez probablement remarqué, je suis quelqu’un d’assez perfectionniste (en tout cas sur les UIs, moins sur les fautes d’orthographes de mes articles …) qui aime utiliser et surtout concevoir des applications aux interfaces graphiques abouties ou comme j’aime le dire polished. Pour considérer une interface comme “parfaite” encore faut-il avoir une liste de l’ensemble des points gageant de la qualité d’une interface : fluidité, esthétique, ergonomie, etc. Au cours de précédents articles, j’ai déjà mentionné beaucoup de ces points “limitants” en donnant des techniques permettant de minimiser leurs impacts ou tout simplement de les supprimer :

Une UI est le produit de plusieurs acteurs. Ces acteurs peuvent être séparés en 4 grands groupes : les ergonomes (qui s’attachent à reproduire/coller voire améliorer la logique des applications Android), les graphistes (qui tentent également de suivre le design/look ‘n ‘feel Android), des intégrateurs (les développeurs utilisant le travail des 2 précédents acteurs pour concevoir l’application finale) et les testeurs (qui sont, je le déplore, souvent les utilisateurs finaux sur Android). En qualité d’ingénieur d’études sur plateformes mobiles (et donc d’intégrateur), je suis quotidiennement amené à concevoir des applications pour la plateforme Android. Il m’arrive donc de devoir intégrer des chartes graphiques dans certaines applications. Je m’aperçois, malheureusement, que les graphistes n’ont très souvent aucune ou peu de connaissances particulières concernant les contraintes inhérentes à Android et au mobile en général. Voici le “top 5″ des points sur lesquels je me bats :

  • Boutons à glossy multiples dans tous les sens. Cela donne généralement de jolis boutons mais rend totalement impossible sa transformation en 9-patches. Les graphistes ne pensent pas que le contenu peut s’agrandir (suite à l’internationalisation par exemple). En conséquence, les images sont étirées “salement” ou leur contenu déborde lorsqu’il est trop important (dans le contexte d’un bouton, je considère que l’option setEllipsize de la classe TextView n’est pas une bonne solution)
  • Utilisation abusive de la transparence. Il est vrai que les designs à la “Web 2.0″ utilisant énormément la transparence sont assez bien adaptés au mobile en terme d’esthétique. Malheureusement, qui dit transparence dit “alpha blending”. Ce processus qui consiste à déterminer la couleur finale d’un pixel transparent en fonction des couleurs des pixels sous-jacents, coûte assez cher et n’est donc pas réellement adapté à des designs en mouvement continuel (ListView par exemple).
  • Positionnement absolu des objets graphiques. Ce problème récurrent démontre tout simplement que les graphistes n’ont pas connaissance des possibilités d’Android en matière d’adaptation aux différentes tailles d’écran. Je me retrouve très souvent confronté à des designs spécialement conçus pour des résolutions de 480×320 pixels en mdpi (160dpi) et en orientation portrait. Que faire de ces designs lorsque l’écran a une résolution/densité différente ou tout simplement lorsqu’il passe en mode paysage?
  • Manque d’états sur les boutons. Les ergonomes le crient haut et fort : un retour utilisateur est indispensable lorsque ce dernier interagit avec le terminal : appui sur l’écran, utilisation de la trackball, etc. J’ai souvent accès à des chartes graphiques n’ayant qu’un seul et unique état pour chaque “contrôle” (ce que j’entends par contrôle c’est un élément graphique sur lequel l’utilisateur peut agir : EditText, Button, etc.). Comment montrer à l’utilisateur que l’appui sur le bouton a bien été pris en compte ? C’est tout simplement impossible. N’oubliez donc pas de définir l’ensemble des états qui peuvent être utilisés (notion de StateListDrawable).
  • Utilisation de dégradés. Une belle charte graphique est généralement composée de dégradés. Bien que le rendu soit parfait sur l’écran de votre ordinateur, il en va bien souvent autrement lorsque le design est intégré à une application Android. Je vous laisse regarder les images ci-dessous pour bien comprendre de quoi je parle et ce que nous allons essayer de contourner dans cet article :

Vous n’arrivez pas à voir le problème qui me chagrine dans de nombreuses applications Android ? Regardons de plus près ! A un tel niveau de zoom vous ne pouvez pas le rater :

Avec les deux images ci-dessus, le problème saute aux yeux : le dégradé est discontinu et un phénomène de bandes apparait. Ce phénomène (qui n’est absolument pas voulu, à mon avis, sur les copies d’écran précédentes) est aussi appellé gradient banding en anglais.

Explication du phénomène

En informatique, une couleur est représentée sous la forme d’un ensemble de 4 valeurs représentant respectivement le pourcentage d’alpha, de rouge, de vert et de bleu. Ainsi une couleur telle que #ff0000 représente le rouge parfait alors que #770000ff indique un bleu semi transparent

La plateforme Android dispose de plusieurs modes de configuration pour représenter les couleurs. Ces modes de configuration sont plus communément appelés “palettes de couleur” (cf android.graphics.Bitmap.Config) et permettent au système de comprendre la représentation des couleurs en mémoire (nombre de bits pour chaque couleur) :

  • ARGB_8888 : Chacune des 4 composantes est codée sur 8 bits. Une couleur de la palette ARGB_8888 occupe donc 4*8 = 32 bits en mémoire. Dans cette palette de couleur, il y a 2^32 = 4 294 967 296 couleurs différentes si on compte l’alpha et 2^(3*8) = 16 777 216 si l’alpha n’est pas compté (ce qui est généralement le cas)
  • ARGB_4444 : Cette palette permet d’obtenir 2^(3*4) = 4 096 couleurs différentes
  • RGB_565 : Cette palette ne gère pas la couche alpha et permet de représenter 2^(5+6+5) = 2^(16) = 65535 couleurs différentes
  • ALPHA_8 : Ne représente que la couche alpha. Cette configuration ne nous intéresse pas dans le cadre de cet article puisqu’elle ne contient que les informations du canal alpha.

Malgré les possibilités d’Android, le matériel limite souvent les possibilités de rendu graphique. En effet, les screenshots ci-dessus montrent des images affichées en mode ARGB_8888. Pourquoi de telles “bandes” sur les dégradés? Ce problème n’est, en réalité, pas inhérent à Android mais aux terminaux mobiles qui utilisent très souvent une palette de 16 bits pour représenter les couleurs à l’écran. Notre émulateur adoré reproduit très bien ce phénomène puisqu’il utilise également une palette restreinte (16 bits). Ainsi, une image affichée dans le mode ARGB_888 n’aura à l’écran qu’un maximum de 2^16 = 65536 couleurs différentes. C’est ce faible nombre de couleurs disponibles qui provoque le gradient banding

Comment contourner le problème ?

Il existe évidemment une méthode permettant de contourner le problème. L’astuce passe par un principe vieux comme l’informatique : le tramage (ou dithering en anglais). Cette technique consiste à “mélanger” les pixels proches les uns des autres pour faire croire à un dégradé plus linéaire.

Activer le dithering par XML

Comme vous le savez, Android permet d’instancier des Drawables par l’intermédiaire de fichiers XML. Imaginons que nous disposions d’une image bitmap_to_dither.png dans res/drawable. Le code XML ci-dessous montre comment créer un BitmapDrawable avec tramage activé (grâce à la propriété android:dither) :

<?xml version="1.0" encoding="UTF-8"?>
<bitmap
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@drawable/bitmap_to_dither"
    android:dither="true" />

De la même façon, une image nine_patch_to_dither.9.png pourra être utilisée et tramée dans un NinePatchDrawable :

<?xml version="1.0" encoding="UTF-8"?>
<nine-patch
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@drawable/nine_patch_to_dither"
    android:dither="true" />

Activer le dithering en Java

Instancier les objets par XML est une fonctionnalité très importante d’Android. Cela permet de clarifier le code Java et surtout de séparer la logique de l’aspect UI. Les exemples disponibles dans le SDK encouragent très fortement l’utilisation de fichiers XML lorsqu’il s’agit de créer un layout, un drawable, une animation, etc. Malheureusement, il arrive parfois (c’est assez rare) que certaines fonctionnalités accessibles en Java ne le soient pas en XML. Dans un tel cas, il est nécessaire de passer directement par le code Java :

Drawable d = getResources().getDrawable(R.drawable.drawable_to_dither).setDither(true);

Le code donné précédemment récupère le Drawable drawable_to_dither puis active le tramage sur ce dernier. Si on considère que d est un Drawable de type GradientDrawable (instancié par XML grâce à la balise <shape />), on s’aperçoit que c’est la seule et unique façon d’activer le dithering sur le gradient. En effet, la classe GradientDrawable ne gère pas l’attribut XML android:dither. Je ne sais pas si cette option a tout simplement été oubliée (ce qui me parait bizarre puisqu’Android existe déjà - publiquement - depuis plus de 2 ans) ou si c’est une volonté de la part de la “team Android” (dans ce cas je ne comprends pas réellement l’utilité puisque l’activation via code est possible et que le tramage ne me semble pas être une fonctionnalité très consommatrice de ressources).

Note :Android (depuis la build Eclair - 2.0) facilite grandement la tâche des développeurs puisque de nombreux Drawable ont l’option “dither” activée par défaut : BitmapDrawable, NinePatchDrawable, etc. Malheureusement, lorsqu’on considère les parts de marchés des systèmes Android à la date de rédaction de cet article (13 janvier 2010), on ne peut pas considérer l’option comme “automatique”. Un bon développeur (c’est à dire vous !) se doit de faire en sorte que l’application fonctionne parfaitement sur la totalité des systèmes postérieurs à Cupcake - 1.5. L’activation de l’option “dither” doit toujours se faire de façon manuelle.

Pré-tramer vos images

La dernière possibilité qui s’offre à vous est de pré-tramer vos images à l’aide de votre éditeur graphique préféré… Cette méthode a l’avantage de soulager légèrement (le tramage à la volée n’est pas une opération extrêmement coûteuse vu la puissance des terminaux actuels) le terminal lors du rendu. L’inconvénient réside dans la difficulté d’effectuer ce pré-tramage. J’ai longtemps cherché des méthodes performantes pour pré-tramer mes images et je pense avoir trouvé deux solutions :

  • La première consiste à séparer les 3 couches rouge, vert et bleu de l’image à tramer. Pour chacune des couches on réduit (avec l’option tramage activée) le nombre de bits autorisés pour représenter les différentes couches (en RVB_565 cela on obtient 5 bits pour le rouge, 6 bits pour le vert et 5 bits pour le bleu). On recombine enfin les 3 couches pour obtenir une image parfaitement tramée. Cette démarche est parfaitement expliquée sur ce site anglophone
  • La seconde technique est beaucoup plus simple puisqu’il s’agit d’utiliser un simple plugin Photoshop. Ce plugin ne permet pas, contrairement la méthode précédente, de choisir avec précision le nombre de bits pour chaque couche de couleur. Le plugin est téléchargeable sur le site de Telegraphics

Le tramage par l’exemple

Enfin me direz-vous ! Vous avez raison ! Fini de discuter. Passons à la pratique avec un exemple concret. L’objectif est de réaliser un splash screen qui s’adapte parfaitement à différentes résolutions, différentes orientations … en clair, un splash screen parfaitement conçu pour Android !

Note : Cet article ne traitera pas de la gestion des différentes densités. Il aurait fallu simplement remplir les dossiers res/drawable-ldpi, res/drawable-mdpi et res/drawable-hdpi avec les images adéquates. Mis à part ce point (non abordé pour faciliter la compréhension) les techniques citées ci-dessous permettent de créer un splash-screen indépendent des densités

Commençons, tout d’abord, par une brève introduction de la scène. Nous sommes en train de réaliser un application qui nécessite d’avoir des design parfait. Nous décidons donc de faire appel à un graphiste freelance qui nous aide à trouver une charte graphique et surtout un splash screen attirant. Le résultat envoyé est une image de 320×480, sorte de design brut comme présenté ci-dessous :

En intégrant de façon “bête et disciplinée” les ressources graphiques reçues à l’aide d’une simple ImageView ayant l’attribut android:scaleType à fitXY, on obtient une image étirée (cela se voit surtout en orientation paysage) et donc totalement non adaptée à une utilisation sur un terminal Android :

Réaliser un bon splash screen passe donc par plusieurs étapes :

  • Commençons par “layouter” le splash screen. Ce que j’entends par “layouter” c’est de découper le splash screen en composants élémentaires qui serviront de vues et seront positionnées grâce à un simple layout (FrameLayout, LinearLayout, etc.). Dans notre exemple, on peut séparer le splash screen en 3 parties : ss_logo, ss_version et ss_author. Le positionnement s’effectue grâce à un FrameLayout
<?xml version="1.0" encoding="utf-8"?>
<merge
	xmlns:android="http://schemas.android.com/apk/res/android">
 
	<ImageView
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:layout_gravity="top|right"
		android:src="@drawable/ss_version" />
 
	<LinearLayout
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:layout_gravity="center"
		android:orientation="vertical">
 
		<ImageView
			android:layout_width="wrap_content"
			android:layout_height="wrap_content"
			android:layout_gravity="center"
			android:src="@drawable/ss_logo" />
 
		<TextView
			android:layout_width="wrap_content"
			android:layout_height="wrap_content"
			android:layout_gravity="center"
			android:text="@string/loading" />
 
	</LinearLayout>
 
	<ImageView
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:layout_gravity="left|bottom"
		android:src="@drawable/ss_author" />
 
</merge>
  • Pour éviter le phénomène de bandes, il convient de tramer à l’aide d’une des méthodes précédemment citées. Notez qu’il n’est pas toujours nécessaire d’effectuer le tramage de vos images. Les images construitent sur une faible palette de couleurs (ss_author par exemple) n’ont pas besoin d’être tramées puisqu’elles ne peuvent pas souffrir de ce fléau de gradient banding. A contrario, ss_logo dispose de plusieurs dégradés (alpha, gris vers blanc) et son rendu est meilleur après tramage

Le fichier ss_background.xml qui permet de définir le dégradé de fond est ajouté au répertoire res/drawable

<?xml version="1.0" encoding="utf-8"?>
<shape
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:shape="rectangle">
 
	<gradient
		android:startColor="#4e525c"
		android:endColor="#31343c"
		android:angle="90" />
 
</shape>

Les images élémentaires du splash screen sont découpées comme suit (le fond des images est en réalité transparent - le gris a été ajouté afin de mieux voir les éléments blancs.

  • Le rendu des dégradés redimensionnables doit être fait en “software”. Dans notre exemple, le fond de notre splash screen s’effectue de façon logicielle en activant l’option android:dither
package com.cyrilmottier.android.metromap;
 
import android.app.Activity;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
 
import com.cyrilmottier.android.metromap.util.Config;
 
public class SplashScreenActivity extends Activity {
 
    private final Handler mHandler = new Handler();
    private static final int SPLASH_SCREEN_DURATION = 1000;
 
    private final Runnable mPendingLauncherRunnable = new Runnable() {
        public void run() {
            Intent intent = new Intent(SplashScreenActivity.this, MetroMapActivity.class);
            startActivity(intent);
            finish();
        }
    };
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.splash_screen);
 
        mHandler.postDelayed(mPendingLauncherRunnable, SPLASH_SCREEN_DURATION);
 
        // Let's activate dithering for the background to prevent banding
        Drawable d = getResources().getDrawable(R.drawable.ss_background);
        d.setDither(true);
        findViewById(android.R.id.content).setBackgroundDrawable(d);
 
    }
 
    @Override
    protected void onPause() {
        super.onPause();
        mHandler.removeCallbacks(mPendingLauncherRunnable);
    }
 
}

A vos Photoshop, Gimp et autres logiciels exotiques de retouche d’images. Vous avez maintenant toutes les cartes en main pour peaufiner vos UI !

Voilà l’année 2010 qui débute et je souhaitais changer légèrement les sujets traités sur ce blog. En effet, la plupart des articles que j’ai pu rédiger sont strictement techniques. Les prochains articles seront très probablement similaires mais d’autres genres de problèmatiques relatives à Android seront abordées (ergonomie, esthétique, marché, etc.). Cet article vient inaugurer ce léger changement en vous présentant une application de ma création disponible depuis hier sur l’Android Market.

Puisqu’on n’appâte pas les gens avec des cailloux (si si j’ai déjà entendu cette expression quelque part …), voici, pour commencer des copies d’écrans de l’application :

Vous l’aurez deviné, l’intérêt fonctionnel de cette application est de présenter aux utilisateurs une carte du métro parisien. Il est évident, que vous expliquer l’utilité d’une telle application serait déplacé de ma part. Je souhaitais plutôt vous faire part des raisons qui m’ont poussé à développer cette application. Si vous souhaitez la télécharger utiliser le QRCode ci-dessous :

Note : A la date de rédaction de cet article, l’application n’est pas encore entièrement compatible avec les densités différentes de 240dpi (le terminal passe automatiquement en mode “compatibilité” mais cela implique un scaling à la volée qui ralentit drastiquement l’application). Sachez que le travail est en cours et que des mises à jour seront poussées dès que possible.

MetroMap est tout d’abord née suite au concours lancé par Archos. Il y a maintenant plus d’un an, cette compagnie française a choisi d’utiliser la plateforme Android pour certains de leurs terminaux (Archos 5 IT par exemple). Cela semblait une bonne idée mais ces derniers ont décidé (je ne sais pourquoi - et je n’ai pas cherché la raison) de réinventer la roue en développant leur propre market place : AppsLib. Pour lancer AppsLib, Archos a lancé un concours de développement consistant à faire une application fonctionnelle sur Archos 5 IT. Si votre application était validée par Archos, vous receviez un bon de réduction de 220€ pour l’achat d’un Archos 5 IT.

La seconde raison qui m’a motivé c’est le manque d’applications réellement finies disponibles pour Android. Je trouve que l’Android Market regorge d’applications toutes mieux les unes que les autres (au moins d’un point de vue fonctionnel). Malheureusement, dès que je teste les applications, j’ai toujours une impression de “non fini”, de “fait à la va-vite”, de “encore 2 jours de développement et l’application est parfaite”, d’”application publiée trop tôt”, etc. J’ai voulu montrer qu’avec un peu de patience et de volonté, il est possible d’obtenir une application jolie et aboutie.

Pour finir, j’ai souhaité montrer à tous mes lecteurs que ce que je poste sur ce blog a une utilité ^^. En effet, la plupart des principes que j’ai utilisés dans MetroMap Paris ont été expliqué dans différents articles techniques publiés sur ce blog. En clair, si vous êtes bon élève voilà le genre d’applications que vous devriez pouvoir au minimum obtenir (je dis “au minimum” car je ne peux que vous souhaiter de faire mieux).

J’espère que vous trouverez cette application intéressante du point de vue UI et je ne peux que vous remercier pour les commentaires et notations que vous effectuerez sur ce blog ou l’Android Market.

J’allais oublier un dernier petit point … le point technique. Vous remarquerez que l’application affiche une image de très grande taille. Tenter de créer une Bitmap à partir d’une image de 2300×2300px ou plus se solde tout simplement sur un OutOfMemoryException (2300*2300*4 = 10,58 Mo). J’ai donc développé une API permettant de contourner le problème (principe de tuiles ou tiles). J’ai cru comprendre que c’est un problème assez récurrent parmi les développeurs Android. L’ouverture du code permettrait de présenter différentes fonctionnalités intéressantes (tiling API, bouncing flings, etc.) et je tenterai de le faire si je trouve mon code assez propre et bien conçu. Si vous êtes intéressé par une telle API, n’hésitez par à laisser un commentaire ci-dessous !

Rien de technique pour cet article ! Je souhaitais simplement souhaiter de joyeuses fêtes à tous les lecteurs de ce blog. L’année 2009 a été tout simplement ce qu’on pourrait appeler une “année Android”. Avec la sortie de pas moins de 4 versions de système (1.5, 1.6, 2.0 et 2.0.1). Le nombre de terminaux sous Android a également suivi cette tendance et j’espère sincèrement (j’en suis même certain) que l’année 2010 sera considérée comme l’année du véritable décollage d’Android…

En 2009 (ou plutôt depuis février 2009) ce sont pas moins de 27 articles techniques sur Android qui ont été posté ici. Au vu de certains des commentaires, je pense avoir atteint en partie l’objectif que je recherchais en lançant ce blog : conseiller les développeurs sur les bonnes pratiques de développement Android. J’espère que vous avez appréciés tous ces articles et que vous continuerez à en lire de nouveaux au cours de 2010.

Bonne fêtes à vous tous