Les ressources
- Ressource : élément statique réutilisable
-
Ressources définies dans des fichiers XML (ou binaires) dans res/ (noms de fichiers [a-z0-9_]+) :
- res/values : déclaration XML (avec i18n) de string, color, dimen, style...
- res/drawable : fichiers multimédias binaires (images, sons)
- res/layout : définition XML d'agencements de vues graphiques (sous forme d'arbre)
-
res/anim : fichiers XML de définition d'animations
- Animation d'interpolation (changement de transparence, échelle, angle de rotation...)
- Animation pour séquences d'images : liste des images avec leur durée d'affichage
- res/xml : pour des fichiers XML divers
- res/raw : pour d'autres ressources sous la forme de fichiers binaires
Référence aux ressources
-
Référencement de chaque ressource par une constante entière dans des classes internes de R.java
- Ce fichier est autogénéré par le script de compilation
- Utilisation de Resources Context.getResources() (Activity hérite de Context) pour obtenir un getter de ressources
-
Quelques exemples :
- getString(R.string.hello_world)
- getStringArray(R.stringarray.messages)
- getColor(R.color.my_nice_color)
- getLayout(R.layout.activity_layout)
- getDimension(R.dimen.world_width)
- getDrawable(R.drawable.card_picture)
- getIntArray(R.intArray.coordinates)
- openRawResource(R.raw.bindata) (retourne un InputStream)
- Référencement d'une ressource au sein d'une autre ressource par @[paquetage:]type/nomDeLaRessource
- Existence de ressources système utilisant le paquetage android (par exemple : @android:color/blue)
-
Accès direct à une vue depuis une activité : View Context.findViewById(int)
- Peut être considéré comme l'équivalent de document.getElementById("...") permettant de récupérer un nœud DOM en JavaScript sur une page HTML
- Type de retour View qu'il faut généralement caster
- Avec Kotlin, import automatique des identifiants de vue utilisables directement comme variables dans le code
- Avec Java, existence de la bibliothèque ButterKnife qui permet l'affectation de vue avec des annotations : @BindView(R.id.title) TextView title;
- Il n'est pas possible simplement et proprement de lister dynamiquement toutes les ressources disponibles sans utiliser l'introspection
Versions de ressources
Principe
- Plusieurs versions d'une même ressource peuvent être proposées dans des répertoire distincts suffixés par une spécification de version
-
Par exemple, où stocker les images pour les japanophones français sur un GPS Android à grand écran utilisé la nuit ?
- Dans le répertoire res/drawable-ja-rFR-xlarge-car-night
-
Critères de version (avec priorités) consultables ici en détails
- Mobile Country Code (code cellulaire de pays)
- Mobile Network Code (code du réseau cellulaire utilisé)
- Langage et région
- Direction d'agencement : ldltr (direction de gauche à droite), ldrtl (de droite à gauche)
- ...
-
Méthode de sélection de version de ressource
- On élimine les versions non-compatibles avec la configuration courante
-
Pour chaque critère, du plus important au moins important, on essaie de trouver une version des ressources le validant
- Si pour un critère, une seule version est trouvée, elle est sélectionnée
- Si aucune version n'est trouvée, on continue avec le critère de priorité immédiatement inférieur sur toutes les versions restantes
- Si plusieurs versions sont trouvées, on continue avec le critère immédiatement inférieur sur les versions sélectionnées
Utilisation pratique
-
Pour l'i18n : on place un fichier res/values-lg/strings.xml avec les versions traduites des chaînes de caractères (lg à remplacer par le code ISO à 2 lettres de la langue)
- Il est possible de préciser une région avec la langue : par exemple fr-rCA pour proposer une traduction québecoise
- ☞ la plupart des IDEs (comme Android Studio) proposent une interface d'édition des traductions
- Le choix de la langue peut être effectué globalement sur le système ou localement pour une application (depuis Android 13)
-
Pour créer des layouts différents pour une activité selon la géométrie de l'écran :
-
res/layout-xlarge/mylayout.xml pour un écran de taille importante (plus de 720x960 dp). Pour information, les différentes tailles supportées sont :
- small : écran de taille supérieure à 320x426 dp, soit 51x67 mm
- normal : écran de taille supérieure à 320x470 dp, soit 51x74 mm
- large : écran de taille supérieure à 480x640 dp, soit 76x102 mm
- xlarge : écran de taille supérieure à 720x960 dp, soit 114x153 mm
- depuis l'API 13, il est possible d'utiliser des qualificateurs pour indiquer la longueur et la hauteur minimale du layout : par exemple res/layout-h720dp-w1024dp pour un écran de taille minimum 720x1024 dp
- res/layout-port/mylayout.xml pour un écran en mode portrait, res/layout-land/mylayout.xml pour un écran en mode paysage
-
res/layout-xlarge/mylayout.xml pour un écran de taille importante (plus de 720x960 dp). Pour information, les différentes tailles supportées sont :
-
Pour personnaliser la layout selon les capacités d'entrées de l'appareil :
- le qualificateur notouch indique l'absence d'écran tactile
- le qualificateur qwerty indique la présence d'un clavier physique (pas forcément en disposition qwerty)
À propos des dimensions
Différents moyens d'indiquer une dimension pour les layouts :
- En nombre de pixels (100px)
- En nombre de pixels densité-indépendante (100dp). Un dp représente un pixel avec une résolution standardisée de 160dpi (160 pixels par pouce). Cela permet d'indiquer une dimension qui a toujours physiquement à peu près la même taille quel que soit l'écran (1dp ~ 159 μm).
- En nombre de pixels échelle-indépendante (100sp). Par défaut un sp correspond à un dp sauf si l'utilisateur a redéfini la taille des fontes utilisées dans les paramètres d'accessibilité. On utilisera donc les sp pour indiquer la taille d'une police.
- En millimètres (16mm)
- En pouces (0.63in). Un pouce (inch) mesure environ 25,4mm.
- En points. Un point représente 1/72 pouces, soit 353μm.
Quelques dimensions d'écrans d'appareils Android :
Les assets
- Placés dans le répertoire assets/ du projet
- L'organisation de ce répertoire est libre (on peut y placer à sa guise toute une hiérarchie de répertoires et fichiers)
-
Les assets sont récupérables à l'exécution avec un AssetManager et il est possible de lister tous les fichiers d'un répertoire présent dans assets :
- String[] assets = context.getAssets().list(path);
-
On peut obtenir un InputStream à partir d'un chemin d'asset pour lire le flux d'octets correspondant
- InputStream is = context.getAssets().open(path)
- Cela peut être utile pour charger des images, lire des ressources binaires embarquées dans l'APK...
Exemple : un gestionnaire de fichiers texte présents dans des répertoires de assets
// Code sample from Coursand [http://igm.univ-mlv.fr/~chilowi/], under the Apache 2.0 License package fr.upem.coursand.licenses; import android.content.Context; import android.util.Log; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.lang.ref.SoftReference; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.TreeMap; /** * Manager for a library of Android assets. * Uses a multiton pattern to store the different libraries. */ public class AssetLibrary implements Iterable<String> { private static final Map<String, AssetLibrary> libraries = new HashMap<>(); public static AssetLibrary getLibrary(Context context, String name) { AssetLibrary lib = libraries.get(name); if (lib == null) { lib = new AssetLibrary(context); lib.load(name, ""); libraries.put(name, lib); } return lib; } private final Context context; /** Files present in the library */ private Map<String, String> entries = new TreeMap<>(); /** Cache for the texts loaded from the library */ private Map<String, SoftReference<String>> textCache = new HashMap<>(); public AssetLibrary(Context context) { this.context = context.getApplicationContext(); } /** Load the file entries present under the assets/$path directory */ private void load(String root, String path) { Log.i(getClass().getName(), "Load root=" + root + ",path=" + path); boolean isFile = true; try { String[] filenames = context.getResources().getAssets().list(path.length() > 0 ? root + File.separatorChar + path : root); for (String filename: filenames) { load(root, path.length() > 0 ? path + File.separatorChar + filename : filename); isFile = false; } } catch (IOException e) { Log.i(getClass().getName(), "Exception while browsing the assets", e); } if (isFile) // if it is not a directory { // it is a file if it is not a directory if (path.length() > 0) entries.put(path, root + File.separatorChar + path); } } @Override public Iterator<String> iterator() { Log.i(getClass().getName(), "entries=" + entries); return entries.keySet().iterator(); } /** * Get a text contained into an asset file. * Be careful if the file is too voluminous since the text is loaded entirely in memory */ public String getText(String key) { String sep = System.getProperty("line.separator"); String text = textCache.containsKey(key) ? textCache.get(key).get() : null; if (text != null) return text; // text is already in the cache try (BufferedReader br = new BufferedReader( new InputStreamReader(context.getAssets().open(entries.get(key)), StandardCharsets.UTF_8))) { StringBuilder sb = new StringBuilder(); int counter = 0; for (String line = br.readLine(); line != null; line= br.readLine()) { if (counter > 0) sb.append(sep); sb.append(line); counter++; } text = sb.toString(); textCache.put(key, new SoftReference<>(text)); return text; } catch (IOException e) { return null; } } }
Gestion des ressources lourdes
- Les magasins d'applications interdisent les APK trop volumineux (>100 Mo pour le Google Play Store)
-
Téléchargement des ressources lourdes par l'application depuis un site web et enregistrement sur l'espace de stockage de l'appareil (idéalement sur une carte microSD)
- Possibilité d'utiliser l'API Google pour créer des APK expansion files téléchargeables depuis le Google Play Store