Note : l’astuce donnée dans ce post n’est pas vraiment relative à Android. C’est, en réalité, une astuce rendue possible par la flexibilité du langage Java. Néanmoins, il s’avère que cette technique est bien souvent utile et méconnue des développeurs Android.
Ne vous est-il jamais arrivé d’écrire des lignes répétitives un peu du genre de celles données ci-dessous?
ImageView mImageViews[] = new ImageView[100]; mImageViews[0] = new ImageView(this); mImageViews[0] = setImageResource(R.drawable.image0); mImageViews[1] = new ImageView(this); mImageViews[1] = setImageResource(R.drawable.image1); // Encore et encore les mêmes lignes ... mImageViews[98] = new ImageView(this); mImageViews[98] = setImageResource(R.drawable.image98); mImageViews[99] = new ImageView(this); mImageViews[99] = setImageResource(R.drawable.image99); |
J’espère pour vous que vous n’avez jamais fait cela ! En effet, en plus de faire grossir le code compilé résultant, ce genre de code source est surtout illisible pour de futurs utilisateurs/relecteurs, sujet à erreurs (copier-coller oblige) et surtout très ennuyant à écrire. Tout bon programmeur tente, dans la mesure du possible de factoriser le code. Pour ce faire, on définit d’abord les constantes au niveau de la classe (j’ai mis trois points parce que je suis feignant et que ça n’a pas de réelle utilité de mettre la vraie suite) :
// Prepare le tableau des identifiants de ressources private static final int IMAGE_IDS = {R.drawable.image0, R.drawable.image1, R.drawable.image2, ..., R.drawable.image98, R.drawable.image99}; |
Il est maintenant possible de factoriser le code via une simple boucle for :
ImageView mImageViews[] = new ImageView[100]; final int count = IMAGES_IDS.length; for (int i = 0; i < count; i++) { mImageViews[i] = new ImageView(this); mImageViews[i].setImageResource(IMAGES_IDS[i]); } |
Une telle méthode a l’avantage certain de minimiser le code “effectif”, d’être beaucoup plus lisible et moins dangeureuse. Malheureusement, elle reste encore assez rébarbative puisque il faut créer le tableau IMAGES_IDS. Pour finir, si on souhaite ajouter de nouveaux drawables, il faut retoucher au code du tableau IMAGES_IDS pour qu’ils soient pris en compte.
Android regroupe, de façon automatique (c’est l’outil aapt qui s’en charge) , l’intégralité des ressources dans un fichier nommé R.java. En d’autres termes, les ressources sont accessibles via le code Java par l’intermédiaire de constantes définies dans ce fichier de ressources. Pour faciliter la récupération de ressources, il est donc possible d’utiliser les Field. Le code précédent devient maintenant :
// On récupère l'ensemble des "champs" de la classe R.drawable Field[] fields = R.drawable.class.getFields(); // Le -1 est nécessaire pour ne pas inclure l'icône de l'application ImageView mImageViews[] = new ImageView[fields.length - 1]; int i = 0; for (Field field : fields) { try { // Pour chaque champ, on récupère sa valeur (c'est à dire l'identifiant de la ressource) int currentResId = field.getInt(R.drawable.class); // Si cet identifiant n'est pas celui de l'icône de mon application if (currentResId != R.drawable.mydemo_icon) { mImageViews[i] = new ImageView(this); mImageViews[i].setImageResource(currentResId); i++; } } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } |
Toute méthode a ses avantages et ses inconvénients. Cette dernière ne déroge pas à la régle. Son avantage indéniable est de supprimer le code à maintenir dans le cas d’ajout ou de suppression de drawable. Lorsqu’on ajoute un nouveau drawable au projet, il est automatiquement pris en compte au prochain lancement du programme. Son inconvénient réside dans sa “globalité”. Imaginons que vous souhaitiez ne prendre un compte qu’un nombre fini (20 par exemple) des 100 drawables de votre projet. La condition d’exclusion devient énorme !.
Pour des noms de fichiers suivant le même schémas (comme dans l’exemple donné : image1, image2, …, image100) il reste possible de faire une exclusion sans devoir tout taper à la main. Par contre cela relève quelque peu du bricolage.
En effet, en passant toujours par le fichier R, on lui récupère les sous classes statiques, et on extrait la sous classe “drawable”. Le code peut ressembler à ça (en considérant que le package est “com.monpackage”) :
private Class getRSubClass(String subclass)
{
Class[] classes = null;
try
{
classes = class.forName(”com.monpackage.R”).getClasses();
}
catch (ClassNotFoundException e1)
{
Log.e(”", e1.toString());
}
for (int i = 0; i < classes.length; i++)
if(classes[i].getName().equals(subclass))
{
return classes[i];
}
return null;
}
ImageView img;
Field field;
try
{
for (int i = 20; i < NOMBRE_IMAGES; i++
{
field = getRSubClass(”com.monpackage.R$drawable”).getField(”image” + i);
img.setImageDrawable(this.getResources().getDrawable(field.getInt(new Object())));
}
}
catch (Exception e)
{
e.printStackTrace();
}