Note : L’article suivant ne traite pas particulièrement de technologies et d’APIs propres à Android. La plupart des APIs mentionnées ci-dessous sont en fait des APIs propres à Java lui-même. Néanmoins, elles sont énormément utiles dans le contexte d’Android … pourquoi ? Eh bien je ne vais pas le dire ici car c’est l’objet de cet article ! Bonne lecture

La programmation sur terminaux mobile est contrainte par un nombre importants de facteurs. L’objectif n’est pas ici de faire une liste exhaustive de tous ces facteurs car je pense l’avoir fait dans de nombreux précédents posts. Nous allons simplement traiter ici d’une des contrainte majeure : la gestion de la mémoire.

La gestion de la mémoire étant omniprésente dans tous systèmes informatique, les plateformes offrents des systèmes de gestion tous plus différents les uns que les autres. Certains environnement sont “garbage collectés” (c’est le cas d’Android) alors que d’autres offrent une gestion de la mémoire manuelle (c’est le cas de l’iPhone par exemple : lorsque vous créer un objet - message alloc - vous devez explicitement mentionner que vous ne l’utilisez plus - message release - pour que le système le supprime). Je ne vais pas rentrer dans un débat sans fin pour dire quel modèle est vraiment le plus avantageux car je pense qu’aucun n’est parfait … tout dépend du contexte dans lequel on les utilise.

En Java, lorsque vous créer un objet, vous créer en réalité une référence vers cet objet. La durée de vie d’un objet Java décrit souvent le schéma suivant :

  • L’objet O est créé et le développeur dispose d’une référence A sur ce dernier
  • L’objet O commence à “vivre” : il est référencé par d’autres objets B, C et D
  • Les objets B, C et D n’utilise plus 0 est donc le compte de référence sur O repasse à 1 (référence A)
  • La référence A n’est plus utilisée et l’objet O devient donc “non référencé” : c’est à ce moment là que le garbage collector considère qu’il est possible de supprimer l’objet de la mémoire.

Laisser le garbage collector gérer lui-même la mémoire facilite grandement le développeur. Malheureusement il arrive parfois que ce dernier prenne ses aises et qu’il utilise trop de mémoire. Le résultat est souvent impitoyable et se résume en une unique Exception que nous devrions tous essayer d’éviter : OutOfMemoryException. Comme son nom l’indique, cette Exception indique qu’il n’y a plus assez de mémoire pour créer l’objet.

Avant d’introduire la solution à ce problème j’aimerai donner la philosophie qui a été choisie sur l’iPhone SDK pour permettre de résoudre ce problème. Le SDK fournit un réalité un centre de notifications : le NSNotificationCenter. Il est possible de s’enregistrer auprès de ce centre de notification et ainsi être notifié de possibles messages du système. Dans notre cas, il suffit d’écouter le message UIApplicationDidReceiveMemoryWarningNotification (oui je sais il est long ce nom … vive l’autocomplétion !) afin de savoir quand il devient urgent de supprimer des objets inutiles/recréables de la mémoire (caches, objets inutilisé, etc.)

Java n’inclut pas ce genre de système. Néanmoins il est possible de gérer la mémoire de façon plus précise en utilisant un des 3 types de référence présentés ci-dessous :

  • Reference : C’est la référence classique. C’est le type de référence par défaut utilisée partout dans Java. La durée de vie d’un objet rattaché par une Reference a été expliqué précédemment
  • WeakReference : C’est une référence qui indique au GC que l’objet référencé peut être supprimé si et seulement si il est référencé uniquement par une ou plusieurs WeakReferences.
  • SoftReference : Un objet référencé par une ou plusieurs SoftReference sera supprimé de la mémoire lorsque le GC va considérer que le système manque de mémoire. C’est donc la référence parfaite pour émuler le comportement décrit ci-dessus. C’est pourquoi on utilise souvent cette référence lors de l’élaboration de caches par exemple.

Il existe encore un autre type de référence nommé PhantomReference mais je ne pense pas qu’elle est une utilité dans vos développements Android. Vous voilà maintenant fin prêt pour parfaire la gestion mémoire de votre application !

Introduction to the GreenDroid library

Note : Before starting with this post I’d like to explain myself about the fact this article has been written in English. Indeed, I have proclaimed, quite a few times, this blog was intended to French speakers only. Actually I have recently changed my mind and here is a brief explanation. When I created this blog I was trying to explain to French speakers how to use and code properly for the Android platform. It had been a truly difficult choice for me because I was and I’m still a big fan of English speeches/talks. Nevertheless, the main reason that pushed me to start writing in French was the fact French documentation dealing with Android was/is very poor. For more than a year, I have written several articles and I really hope you have appreciated all of them. Nowadays, I want to talk about innovative concepts and/or technologies which, I think, will be useful to many users/developers all around the world. As a result, I have to write my posts using the most common language on Earth: English. I’m sure, French speakers will succeed in reading this blog and I’d like to warn them: “Je n’ai pas prévu d’arrêter d’écrire des posts en français !”.

I’m really glad to introduce you to my first attempt of a “true” development library for Android! As you may have noticed it is named “GreenDroid” and is intended to help people in designing Android applications. Keep in mind, it is still a work in progress! Let’s explain everything about GreenDroid before diving into it!

The main purpose of GreenDroid

As some of you may know, I’m a mobile software engineer and I’ve already developed on many constrained platforms. Each platform I have used have inherent advantages or disadvantages. For instance, Android development rocks because it is based on the Java programming language which is easy to use. On the other hand, I’ve also heavily used the iPhone SDK and I’m a huge fan of its simplicity - it looks like the Mac OS X user environment: simple and efficient - and most of all, the fact you can create your own libraries.

For a long time I have coded on the Android platform by doing the same things over and over again. To prevent me from doing mistakes and copy&pastes I’ve decided to initiate a personal project called GreenDroid. Here are the main purposes of this project:

  • Prevent loosing time at copying the same snippets of code over and over again.
  • Try to make Android applications alike. The openness of the Android operating system makes it less coherent. Seing so many applications that use totally different ergonomics or designs drives me mad. Indeed, I truly think, Android applications need coherency in order to get more and more users around the world. GreenDroid is kinda of a try to bring coherency to your applications and therefore to the Android environment. Google recently tried to show a path to “great UX and UI” with the Twitter and Google I/O applications - A talk about Android UIs will be given at Google I/O 2010 and I’m looking forward to watch it; I would like to see if Google has understood Android developers need help creating simple and smart interfaces!
  • Help developers to code highly functional applications. The Android framework may look like “over-engineered” sometimes. Actually, I believe this is a direct consequence of the fact you can do almost everything you want. Unfortunatly, this openness (again!) makes it harder to apprehend. Let’s say for instance, you are a beginner and wants to develop your own application. You’ll have to read a lot of documentation in order to be “up and ready”. GreenDroid makes development a lot easier without dicreasing the powerfulness of the amazing Android framework!
  • Leverage the power of the Android framework. Developing on the Android platform may be pretty easy if you’re not taking care of the resources you’re using. Trying to optimize your application is quite hard sometimes and is a very demanding task. GreenDroid has been developed to be as efficient as possible by integrating basic optimizations.
  • Use as much XML as possible. It’s not a mystery to anybody. Android UI development is based on amazing techniques. Layouts and views are defined in XML and automatically inflated by the system. Being an “easy-to-read-for-humans” format, XML is very used among Android developers. GreenDroid puts XML in the middle of the library and takes advantage of all amazing possibilities offered by Android XML files.

Why have I chosen the “GreenDroid” name?

When I started this project, I have spent a few minutes to think about an appropriate name. At the first glance, I was like “Who cares? It’s a personal project and I will never share it!” but once I realized this project could be useful for a lot of people I decided to think about a clear and smart name. The thought ended up on the name GreenDroid which can be seen as a contraction of the “green” color and the name “Android”. I suppose I don’t need to explain why the library name contains an occurence to Android … so I’ll explain what the “green” word stands for:

  • Green because it’s an “ecological” library. One of the main purpose of this library, as said previously, is to be as efficient as possible by helping developers to optimize their applications. The green color refers to ecology and I thought using it was a good explanation to the library: develop preserving all resources that are available to you!
  • Green because Android is nothing without green Come on guys! You all know the Android mascot is always green. Seeing it wearing other colors makes all of us sad. I couldn’t name my library using another color!
  • Green because I like being green!

How hard it is to create an Android library

The answer is quite simple and straight forward; it’s impossible! Why? Because it is impossible to bundle R.java in external jars … :(. This R.java file contains a set of references to external resources as simple integers int. The only external R.java available in your application is the one bundled with the Android framework.

Here are some ways I know that try to overcome that R.java problem:

  • Using no resource. The problem with that method is it very painful to code without XML. Being convinced Android developers enjoy XML, I’m sure there are just a few people creating their GUI using Java code. Personally, I love the LayoutInflater and its simplicity.
  • Using methods such as Resources.getIdentifier(String name, String defType, String defPackage). Unfortunaty those methods are quite slow to execute which is the opposite of GreenDroid’s main purpose. It’s a great alternative but makes the Java almost unreadable and still force the developer to manually copy resources from the library to his/her project.
  • Copying manually all resources from a project to another. This is basically the method I decided to use. The only difference with the “dumb way” is I created a Python (I’m still a newbie with Python - but I really like it) script that applies the GreenDroid library to your application by switching automatically to the project package name. Developing a graphical library being impossible, I have developed many features that will “fake” GreenDroid is a library. This is an approach I have designed alone and I’m still listening to your ideas to overcome that R problem of the Android SDK

Note : I haven’t taken a look at the Maven Android plugin yet. If one of you think it could be a great way to overcome the R problem, feel free to reply

How to use GreenDroid ?

In order to use GreenDroid you have to do the following steps:

  • 1. Download the GreenDroid library on your computer with a simple:
    git clone http://github.com/cyrilmottier/GreenDroid.git
  • 2. Apply GreenDroid to your project: Go to the scripts/ folder of the GreenDroid folder. Run the script named “greendroid.py” (make sure you can run it - chmod +x greendroid.py) to apply the library to your project :
    ./greendroid.py apply <project_directory>
  • 3. By default, the GreenDroid theme inherits from @android:style/Theme. If your project inherits from a different theme, you’ll have to modify the GreenDroid library on your own (and do that everytime you are updating GreenDroid as updating delete all of your changes). Open the res/values/gd_themes.xml and replace the parent theme @android:style/Theme with your own theme
  • 4. You finally need to make your project use the GreenDroid base theme. In your Android Manifest, go to the application tag and add android:theme="@style/Theme.GreenDroid" as a new attribute (if this attribute already exist override it: if you processed the step 3 correctly, you will have a theme that inherits from this theme)

Hello GreenDroid !

As I said previously, GreenDroid is pretty much a work in progress. For now, it mostly enhances ListView and Adapters by helping the user creating advanced UIs. I have thousands of ideas about how to make Android development easier and how to create easier amazing UIs but I don’t have a lot of free time to develop those importants features…

Items-based ListViews

Creating ListView with various cells can be quite painful. You have to understand perfectly how an Adapter work. You also need to create layout for all of those cells and this is a very boring task. Creating advanced ListViews with GreenDroid is very simple and relies on a simple notion: the item. An item is a wrapper of data that contains everything that will be displayed in the associated item view. The following snippet of code shows you how to use an ItemAdapter:

package com.cyrilmottier.android.gdcatalog;
 
import greendroid.widget.ItemAdapter;
import greendroid.widget.item.DescriptionItem;
import greendroid.widget.item.DrawableItem;
import greendroid.widget.item.Item;
import greendroid.widget.item.ProgressItem;
import greendroid.widget.item.SeparatorItem;
import greendroid.widget.item.TextItem;
import greendroid.widget.item.ThumbnailItem;
 
import java.util.ArrayList;
import java.util.List;
 
import android.app.ListActivity;
import android.os.Bundle;
import android.os.Handler;
 
public class BasicItemActivity extends ListActivity {
 
    private final Handler mHandler = new Handler();
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
 
        List<Item> items = new ArrayList<Item>();
 
        items.add(new SeparatorItem("Class 1"));
        items.add(new ThumbnailItem("Powered paragliding", "aka paramotoring", R.drawable.class1));
        items.add(new DescriptionItem("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tempus consequat leo, et tincidunt justo tristique in."));
 
        items.add(new SeparatorItem("Class 2"));
        items.add(new DrawableItem("Trikes", R.drawable.class2));
        items.add(new DescriptionItem("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tempus consequat leo, et tincidunt justo tristique in."));
 
        items.add(new SeparatorItem("Class 3"));
        items.add(new ThumbnailItem("Multi-axis", "Looks like a tiny place", R.drawable.class3));
        items.add(new DescriptionItem("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tempus consequat leo, et tincidunt justo tristique in."));
 
        items.add(new SeparatorItem("Class 4"));
        items.add(new ThumbnailItem("Auto-gyro", "A scary helicopter", R.drawable.class4));
        items.add(new DescriptionItem("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tempus consequat leo, et tincidunt justo tristique in."));
 
        items.add(new SeparatorItem("Class 5"));
        items.add(new DrawableItem("Hot air baloon", R.drawable.class5));
        items.add(new DescriptionItem("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tempus consequat leo, et tincidunt justo tristique in."));
 
        final Item item1 = new SeparatorItem("Class 6");
        final Item item2 = new TextItem("Airbus/Boeing planes");
        final Item item3 = new DescriptionItem("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tempus consequat leo, et tincidunt justo tristique in.");
        items.add(item1);
        items.add(item2);
        items.add(item3);
 
        final ProgressItem progressItem = new ProgressItem("Removing intruders", true);
        items.add(progressItem);
 
        final ItemAdapter adapter = new ItemAdapter(this, items);
        setListAdapter(adapter);
 
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                adapter.remove(item1);
                adapter.remove(item2);
                adapter.remove(item3);
                adapter.remove(progressItem);
                adapter.insert(new ThumbnailItem("Ultralight aviation", "List of French 'ULM' classes", R.drawable.greendroid_icon), 0);
                adapter.notifyDataSetChanged();
            }
        },8000);
 
    }
}

XML generated items

Android has an amazing feature that consists on inflating data and views from an XML file. I extended it to the GreenDroid library. With GreenDroid you can create and inflate static items and display those items in a simple ListView:

<?xml version="1.0" encoding="utf-8"?>
<item-array
    xmlns:greendroid="http://schemas.android.com/apk/res/com.cyrilmottier.android.gdcatalog">
 
    <text-item
        greendroid:text="TextItem" />
    <text-item
        greendroid:enabled="false"
        greendroid:text="TextItem (disabled)" />
    <text-item
        greendroid:text="TextItem Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tempus consequat leo, et tincidunt justo tristique in." />
    <longtext-item
        greendroid:text="LongText Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tempus consequat leo, et tincidunt justo tristique in." />
    <description-item
        greendroid:text="DescriptionItem Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tempus consequat leo, et tincidunt justo tristique in." />
    <separator-item
        greendroid:text="SeparatorItem" />
    <progress-item
        greendroid:isInProgress="true"
        greendroid:text="ProgressItem" />
    <drawable-item
        greendroid:enabled="false"
        greendroid:text="DrawableItem (disabled)"
        greendroid:drawable="@drawable/greendroid_icon" />
    <drawable-item
        greendroid:text="DrawableItem (with no drawable)" />
 
    <subtitle-item
        greendroid:text="SubtitleItem"
        greendroid:subtitle="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tempus consequat leo, et tincidunt justo tristique in." />
 
    <subtext-item
        greendroid:text="SubtextItem"
        greendroid:subtext="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tempus consequat leo, et tincidunt justo tristique in." />
 
    <thumbnail-item
        greendroid:text="ThumbnailItem"
        greendroid:subtitle="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tempus consequat leo, et tincidunt justo tristique in."
        greendroid:thumbnail="@drawable/greendroid_icon" />
 
    <thumbnail-item
        greendroid:enabled="false"
        greendroid:text="ThumbnailItem (disabled)"
        greendroid:subtitle="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tempus consequat leo, et tincidunt justo tristique in."
        greendroid:thumbnail="@drawable/greendroid_icon" />
 
</item-array>

Thanks to XML, the associated Java code is very clear and simple:

package com.cyrilmottier.android.gdcatalog;
 
import greendroid.widget.ItemAdapter;
import android.app.ListActivity;
import android.os.Bundle;
 
public class XmlItemActivity extends ListActivity {
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
 
        ItemAdapter adapter;
        try {
            adapter = ItemAdapter.createFromXml(this, R.xml.items);
            setListAdapter(adapter);
        } catch (Exception e) {
            e.printStackTrace();
        }
 
    }
 
}

The GreenDroid library can be download on Github:

http://github.com/cyrilmottier/GreenDroid

The library includes a project called GDCatalog that uses the GreenDroid library. It has been develop to help developers understanding how to use GreenDroid in their projects. Keep it mind, the GDCatalog project doesn’t include the library. Prior to launch it, apply the library using the greendroid.py script. If you kept the directory hierarchy of the Git repository you need to execute the following command from the root directory:

cd GreenDroid/scripts && ./greendroid.py apply ../../GDCatalog

Eclipse may not see the new files that have been added. In order to refresh the project, right-click on the project and select “Refresh”.

Happy coding !

Les lecteurs de ce blog l’auront très problablement remarqué, les sujets que j’aime traiter ici sont toujours assez techniques ou traitent de l’ergonomie et de l’expérience utilisateur sous Android (c’est un point qui me tient particulièrement à coeur car je déteste voir ces applications qui viennent littéralement polluer l’Android Market en ne respectant pas - voire en allant TOTALEMENT à l’encontre - la logique et les démarches d’Android. Pour changer j’avais envie de donner mon ressenti sur les dernières actualités du monde Android.

Twitter pour Android

Personne n’a pu rater cette sortie fracassante de la version officielle de l’application Twitter pour Android. Les ressentis sont unanimes : cette application est une vraie tuerie ! Personnellement j’ai vraiment été impressionné par cette application car elle est très proche de l’ergonomie et du design que je me fais d’une bonne application Android :

  • Design sobre : le design est totalement adéquat à un écran de téléphone mobile, il est sobre, correspond parfaitement au couleur et à l’identité de Twitter. Le tout est bien sûr animé ce qui donne une touche de “vivant” à l’application. Malgré un thème totalement “twiterrisé”, l’entreprise à l’oiseau n’a pas oublié de respecter les notions d’états pressé, focusé, etc.
  • Ergonomie simple et efficace : l’information est visible rapidement. La prise en main est d’une simplicité enfantine et les écrans s’enchainent avec perfection.

En clair, Twitter est un vrai coup de coeur pour moi et pour beaucoup d’autres. Bravo Twitter et bravo Android bien sûr ! J’apprécie voir ce genre d’applications sortir sur la plateforme au robot vert car cela montre bien que le framework n’est absolument pas bloquant d’un point de vue possibilités techniques ou graphiques (c’est une idée pourtant reçue et qu’on n’hésite pas à me mentionner régulièrement …).

Google I/O

Je retiens mes larmes lorsque j’écris l’expression “Google I/O”. Pourquoi ? Tout simplement parce que c’est exactement le genre d’évènement où j’aimerai me trouver. Malheureusement, le coût financier a eu raison de moi … Je ne parle absolument pas du coût de l’entrée mais plutôt de la logistique qui entoure cet évènement. J’aurais volontier payé 400 ou 500$ rien que pour rencontrer les brillant ingénieurs de chez Google et/ou les fans des services et technologies Google. Malheureusement les 1000€ (minimum) d’avion et de logistique annexe (hôtels, nourriture, transport, etc.)

La disponibilité souvent rapide des vidéos des sessions techniques est une aubaine pour les développeurs. Cela permet d’être rapidement à jour sur les dernières avancées des technologies Google. Je reste scotché à mes flux RSS pour être notifié de leur disponibilité !

Pour finir, il est clair que cette édition des Google I/O sera l’occasion d’annoncer des nouveautés. Après “Google Waves” l’année dernière, je pense sincérement que cette année sera la part belle à Android. Les derniers changements de comportement de Google vis à vis des releases Android sont parfaitement en accord avec ma vision de l’avenir d’Android. Google commence à réduire la vitesse de mise à jour de son système d’exploitation mobile. Cela laisse d’une part le temps aux constructeurs d’effectuer des mises à jour de leurs terminaux et limite d’autre part la fragmentation (moins de version d’Android dans la nature). Je suis impatient de voir ce que les ingénieurs de Google nous ont concocté pour cette prochaine mise à jour qui s’annonce gigantesque !

Flash ou pas Flash

Question délicate que de discuter de la nécessité ou non d’avoir Flash sur nos téléphones mobiles. Mon avis sur la question est assez simple : je ne suis pas en faveur de la présence de Flash sur nos beaux terminaux Android. Je crois, en effet, qu’HTML5/CSS3 doit être au coeur de la modernisation du web et je le perçois comme un parfait remplacant à Flash.

Il semblerait que la prochaine version d’Android incorpore la prise en charge totale de Flash … cela va sans dire que ça me déçoit :). J’aurais sincèrement préféré des améliorations du moteur de rendu (encore assez loin des possibilités et de la fluidité offertes par Safari Mobile) qu’une intégration pure et simple de Flash. J’ai aussi un peu de mal à comprendre la logique de Google qui a toujours souhaité aller de l’avant et essayer de limiter de plus en plus l’utilisation de Flash. On se souviendra, par exemple, de Youtube commenant à afficher des vidéos sans utiliser Flash ou Chrome qui est un très grand acteur de l’évolution d’HTML5/CSS3 …

Conclusion

Comme vous vous en douter, l’objectif de ce post n’était pas de lancer une guerre mais tout simplement de donner mon ressenti. N’hésitez pas à laisser un commentaire afin de donner votre opinion sur les différents points abordés précédemment. A très bientôt pour un post un peu particulier …

Note : L’article ci-dessous traite principalement des optimisations sur les Views. Ne perdez pas à l’esprit que les concepts décrits ici sont aussi applicables à toutes notions graphiques sur Android (Drawable, Canvas, etc.)

Une belle interface graphique passe généralement par une utilisation souvent excessive de la transparence dans le design. Lorsqu’on regarde de plus prêt le code source d’Android et notamment les ressource graphiques disponibles dans le dossier res/drawable (images, 9-patchs, etc.), on se rend même compte qu’il est difficile de “vivre” sans transparence. C’est une des raisons pour laquelle le format PNG24 est largement préconisé dans le développement sous Android (le fait que ce soit un format ouvert et efficace joue également énormément dans la balance). Développer une interface graphique qui s’adapte à différentes tailles d’écran mais sans avoir accès à une notion de transparence s’avère malheureusement un calvaire et ne sera pas sans rappeler le développement web sous IE6 il y a encore peu de temps …

La transparence (ou parfois appelée “l’alpha”) engendre quelques conséquences sur l’interface graphique. En effet gérer l’alpha signifie forcément faire des calculs supplémentaires pour déterminer la couleur finale d’un pixel. Imaginons par exemple avoir 2 couches à superposer : une première couche (couche 1) totalement verte (#0f0) et une seconde (couche 2), qu’on pose sur la précédente, rouge transparent (#7f00). Le rendu s’effectue donc en 2 phases :

  • On applique/dessine la couche verte (couche 1)
  • On dessine ensuite la couche rouge transparente en effectuant, pour chaque pixel, un calcul permettant de déterminer la couleur résultante en fonction de actuel l’état (de la couleur) du pixel de coordonnée (x, y) à l’écran et du pixel (x, y) à appliquer.

Déterminer la couleur résultante passe par un calcul qui s’effectue sur chaque pixel. Bien que ce calcul se fasse via JNI (en C) - ou même parfois de façon matérielle, il n’en reste pas moins que c’est un calcul coûteux. L’utilisation de la transparence dans vos interfaces graphiques est donc forcément synonyme de “baisse de la rapidité/fluidité”. Votre objectif est donc maintenant de déterminer la valeur de cette baisse …

Une optimisation instaurée dans l’API level 7 sur View est isOpaque. Elle permet tout simplement d’informer le système si la vue est opaque (sans aucune transparence) ou non. Cette méthode peut sembler inutile mais elle peut pourtant permettre de nombreuses optimisations graphiques. En effet, si une vue opaque recouvre intégralement une autre vue, il n’est pas nécessaire de dessiner la vue sous-jacente. Les premières version d’Android n’intégraient pas ce mécanisme et redessinaient de façon totalement stupide l’intégralité des vues à chaque passe de dessin. Cette méthode apporte donc quelques optimisations permettant d’accélérer votre UI.

N’allez pas vous tracassez pour vos interfaces graphiques. Il n’est vraiment pas nécessaire de supprimer toute occurrence à la transparence dans vos UIs tant que ces dernières sont statiques. L’intérêt d’une telle optimisation est surtout applicable lorsque les vues sont animées car c’est à ce stade que les ralentissements peuvent se faire sentir. La ListView est probablement le widget le plus concerné par ces problèmes.

Ce dont je vais parler ci dessous peut paraitre évident pour la plupart des développeurs Android mais c’est pourtant une notion qui me parait mal connue ou mal utilisée. Le notion que je mentionne ici, c’est le type de vue des items d’une ListView.

La ListView est un composant permettant d’afficher un ensemble de données similaires sous forme de vue scrollable. C’est un widget élémentaire dans n’importe quel système d’exploitation mobile car il permet d’afficher un grand nombre de données un minimisant l’impact mémoire (seules les vues affichées à l’écran sont dans la hiérarchie de vues, système de réutilisation des vues, etc.) et en rendant la lecture par l’utilisateur la plus optimale possible (cellules souvent larges, gestures avancés comme le scroll ou le fling, etc.). Une ListView s’utilise, en général, pour afficher un nombre important de données du même type. Néanmoins, il est possible d’inclure des données un peu “exotiques” pour casser l’impression d’uniformité. Un exemple récurrent serait de de séparer les données ayant un trait commun.

Imaginons que nous ayons une longue liste de titre de films et de séries. Pour faciliter la lecture et la recherche, il peut être intéressant de trier ces vidéos par type et donc d’afficher un séparateur pour chaque section (qui peut par la même occasion casser l’impression de “bloc” d’une liste). Le code ci-dessous donne une implémentation de cette liste statique (code disponible ici) :

package com.cyrilmottier.android.itemviewtype;
 
import android.app.ListActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
 
public class MainListActivity extends ListActivity {
 
	private static final int ITEM_VIEW_TYPE_VIDEO = 0;
	private static final int ITEM_VIEW_TYPE_SEPARATOR = 1;
	private static final int ITEM_VIEW_TYPE_COUNT = 2;
 
	private static class Video {
		public String title;
		public String description;
 
		public Video(String title) {
			this(title, "bla bla");
		}
 
		public Video(String title, String description) {
			this.title = title;
			this.description = description;
		}
	}
 
	private static final Object[] OBJECTS = { "Movies",
			new Video("Iron Man 2"), new Video("Adèle Blanc-Sec"),
			new Video("Twilight - Chapitre 3 : hésitation"),
			new Video("Green Zone"), new Video("Shrek 4, il était une fin"),
			new Video("L'Amour c'est mieux à deux"),
			new Video("Sex and the City 2"), new Video("Predators"),
			new Video("Inception"), "Series",
			new Video("Dr House (Docteur House)"), new Video("True Blood"),
			new Video("Smallville"), new Video("Sanctuary"),
			new Video("Desperate Housewives"),
			new Video("Spartacus: Blood and Sand"),
			new Video("Lost, les disparus"), new Video("Stargate Universe"),
			new Video("How I Met Your Mother") };
 
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
 
		setListAdapter(new VideoAdapter());
	}
 
	private class VideoAdapter extends BaseAdapter {
 
		@Override
		public int getCount() {
			return OBJECTS.length;
		}
 
		@Override
		public Object getItem(int position) {
			return OBJECTS[position];
		}
 
		@Override
		public long getItemId(int position) {
			return position;
		}
 
		@Override
		public int getViewTypeCount() {
			return ITEM_VIEW_TYPE_COUNT;
		}
 
		@Override
		public int getItemViewType(int position) {
			return (OBJECTS[position] instanceof String) ? ITEM_VIEW_TYPE_SEPARATOR
					: ITEM_VIEW_TYPE_VIDEO;
		}
 
		@Override
		public boolean isEnabled(int position) {
			// A separator cannot be clicked !
			return getItemViewType(position) != ITEM_VIEW_TYPE_SEPARATOR;
		}
 
		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
 
			final int type = getItemViewType(position);
 
			// First, let's create a new convertView if needed. You can also
			// create a ViewHolder to speed up changes if you want ;)
			if (convertView == null) {
				convertView = LayoutInflater
						.from(MainListActivity.this)
						.inflate(
								type == ITEM_VIEW_TYPE_SEPARATOR ? R.layout.separator_list_item
										: R.layout.video_list_item, parent,
								false);
			}
 
			// We can now fill the list item view with the appropriate data.
			if (type == ITEM_VIEW_TYPE_SEPARATOR) {
				((TextView) convertView).setText((String) getItem(position));
			} else {
				final Video video = (Video) getItem(position);
				((TextView) convertView.findViewById(R.id.title))
						.setText(video.title);
				((TextView) convertView.findViewById(R.id.description))
						.setText(video.description);
			}
 
			return convertView;
		}
 
	}
}

Les différences majeures entre la version présentée ci-dessus et une liste “classique” se situe au niveau des méthodes :

  • getViewTypeCount() : Implémentée par l’Adapter afin d’informer la ListView du nombre total de types de vues. Dans notre cas, nous avons bien 2 types de vues : les vues séparateur et les vues “vidéos”
  • getItemViewType(int position) : Retourne le type de la vue à la position position. Le type d’une vue est représenté par un entier compris entre 0 (inclus) et getItemViewCount() (exclus).

Le code de getView ressemble fortement à l’implémentation habituelle :

  • Si convertView est nulle, il faut créer la vue adaptée
  • On applique la donnée à la cellule
  • On retourne la cellule

Cette facilité d’utilisation est une conséquence directe de l’utilisation de type d’items. En effet, il n’est pas nécessaire de vérifier le type de la convertView pour savoir si cette dernière est une vue “séparateur” ou une vue “vidéos”. La ListView s’assure, elle même, du type de la convertView.

Il existe d’autres techniques permettant d’inclure des séparateurs dans vos listes comme l’inclusion d’un entête à l’ensemble des cellules (entête dont le flag de visibility est mis à View.GONE lorsque la vue n’est pas en début de section) mais celle présentée ici à l’avantage d’être très proche de l’utilisation classique des ListView. Avec cette nouvelle astuce dans votre sac, vous allez maintenant pouvoir présenter l’information aux utilisateurs de la façon la plus ergonomique et user-friendly possible !