Android, A Complete Course, From Basics to Enterprise Edition

Les Ressources.

Android, A Complete Course, From Basics to Enterprise Edition. 1

Les Ressources. 1

Le cœur du système. 1

1       Les ressources. 1

1.1         Fichiers de ressources. 1

 Cet article est extraie du livre « Android, A Complete Course », disponible sur Android2ee.com.

Les exemples ou les programmes présents dans cet ouvrage sont fournis pour illustrer les descriptions théoriques. Ce code est libre de toute utilisation mais n'est pas distribuable.

La distribution du code est reservée au site :

http://android2ee.com.

La propriété intellectuelle du code appartient à :

Mathias Séguy

L’utilisation de ces codes est sous votre unique responsabilité et personne d’autre que vous ne pourra être tenu responsable des préjudices ou dommages de quelques natures que ce soit pouvant résulter de son utilisation.

Tous les noms de produits ou marques cités dans cet ouvrage sont des marques déposés par leurs propriétaires respectifs.

Publié par http://android2ee.com

Titre Original : Android, A Complete Course, From Basics to Enterprise Edition. Édition Française.

ISBN : 979-10-90388-00-0

Copyright © 2011 by Mathias Séguy

 

Aucune représentation ou reproduction, même partielle, autre que celles prévues à l’article L. 122-5 2° et 3° a) du code de la propriété intellectuelle ne peut être faite sans l’autorisation expresse de Mathias Seguy ou, le cas échéant, sans le respect des modalités prévues à l’article L. 122-10 dudit code

Le cœur du système

1         Les ressources

Nous pouvons distinguer plusieurs types de ressources :

·         Les fichiers de ressources de l’application (Images, Chaines de caractères, layout, xml) qui dépendent du contexte (langue française, taille de l’écran, mode portrait/paysage…)

·         Les bases de données

·         Les fichiers préférences

·         Les fichiers pour la lecture et la lecture/écriture sans contexte.

Toutes ces ressources se trouvent dans votre application sous res/.

1.1       Fichiers de ressources

Ces types de ressources sont celles utilisées par l’application et interprétées par le système Android. Elles sont de quatre types:

·         res\drawable pour les images

·         res\raw pour les ressources brutes, les fichiers sans contexte dont nous parlerons au chapitre suivant

·         res\values pour les chaînes de caractères, les couleurs, les tableaux et les dimensions

·         res\xml pour les fichiers xml.

1.1.1       Gestion des chaînes de caractères : l’internationalisation

Pour la gestion de l’internationalisation, la convention veut que le dossier contienne la langue associée : res\values-fr pour le français, res\values-en pour l’anglais et ainsi de suite. « en » et « fr » sont, respectivement, les valeurs usuelles des langues anglaise et française lors de l’internationalisation. Sa gestion devient alors transparente au développeur.

Il y a cinq types de chaînes, les normales, celles avec paramètres, celles au singulier/pluriel, celles en tableaux  et celles au format HTML dont nous ne parlerons pas.

Ainsi le fichier suivant présente les quatre types de chaînes qui nous intéressent :

<?xml version="1.0" encoding="utf-8"?>

<resources>

                <!--Simple string-->

                <string name="hello">Hello World, ResourcesTuto!</string>

                <string name="app_name">ResourcesTuto</string>

                <string name="simpleString">Good Morning My name is Bond, James Bond</string>

 

                <!--String with parameters ($s for string, $d for decimal (any decimal: integer, double, float...)-->

                <string name="param_message">Hello, i love %1$s, i hate %2$s, i am %3$d years old</string>

 

                <!--String array-->

                <string-array name="i_like_array">

                               <item>chocolate</item>

                               <item>television</item>

                               <item>internet</item>

                               <item>nicotine</item>

                               <item>hug</item>

                               <item>Santa Claus</item>

                </string-array>

 

                <!--Plurals String-->

                <plurals name="singleOrPlural">

                               <item quantity="one">I am alone on moon</item>

                               <item quantity="other">I am 6 900 000 000 on earth</item>

                </plurals>

 

</resources>

Les chaînes simples s’utilisent soit dans le code :

getString(R.string.simpleString)
 

Soit dans le layout.xml pour déclarer un composant en utilisant son identifiant:

<TextView

                android:id="@+id/simpleStringTV" 

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="@string/hello"

    />

Les chaînes avec paramètres s’utilisent avec un formateur :

Dans le fichier strings.xml, nous avons déclaré la chaîne :

<!--String with parameters ($s for string, $d for decimal (any decimal:integer, double, float...)-->

<string name="param_message">Hello, i love %1$s, i hate %2$s, i am %3$d years old</string>

 

Nous la manipulons ainsi en java:

 

//Then instanciate a string with paremeter

String parameterString=String.format(getString(R.string.param_message),"peace","war",2);

//And set the text view

TextView textViewWithParam=(TextView)findViewById(R.id.parameterStringTV);

textViewWithParam.setText(parameterString);

 

Les chaînes peuvent être regroupées sous forme de tableaux :

                <!--String array-->

                <string-array name="i_like_array">

                               <item>chocolate</item>

                               <item>television</item>

                               <item>internet</item>

                               <item>nicotine</item>

                               <item>hug</item>

                               <item>Santa Claus</item>

                </string-array>

 

Dans le code, celui-ci se manipule ainsi :

//Instanciate the resources object:

Resources resources=getResources();

String[] planets = resources.getStringArray(R.array.i_like_array);

 

Les chaînes peuvent aussi posséder un genre singulier ou pluriel :

<?xml version="1.0" encoding="utf-8"?>

<resources>

    <plurals name="numberOfSongsAvailable">

        <item quantity="one">One song found.</item>

        <item quantity="other">%d songs found.</item>

    </plurals>

</resources>

Qui s’utilisent dans le code ainsi :

int count = getNumberOfHuman();

Resources res = getResources();

String humans = res.getQuantityString(R.plurals.singleOrPlural, count);

Il faut faire attention aux ‘ et "

<string name="good_example">"This'll work"</string>

<string name="good_example_2">This\'ll also work</string>

<string name="bad_example">This doesn't work</string>

1.1.1.1       Le cas des chaînes de caractères HTML

Ce cas est un cas particulier car le code HTML est du texte à balises, ce qui perturbe le parser xml et empêche la compilation de votre code.

Une manière de contourner le problème est de mettre la balise &lt en lieu et place du <.

Un exemple, dans votre fichier string.xml :

<string name="html_head">&lt;html>&lt;body>&lt;table>&lt;tr>&lt;th width=\"50%\">Jour&lt;/th>&lt;th>Basse&lt;/th>&lt;th>Haute&lt;/th>&lt;th>Tendance&lt;/th>&lt;/tr></string>

<string name="html_loop">&lt;tr>&lt;td align=\"center\">%1$s&lt;/td>&lt;td align=\"center\">%2$s&lt;/td>&lt;/td>&lt;td align=\"center\">%3$s&lt;/td>&lt;td>&lt;img src=\"%4$s\">&lt;/td>&lt;/tr></string>

<string name="html_foot">&lt;/table>&lt;/body>&lt;/html></string>

Le code Java pour utiliser cette chaîne :

StringBuffer bufResult = new StringBuffer(getResources().getString(R.string.html_head));

String tempLine;

for (Forecast forecast : forecasts) {

tempLine=String.format(getResources().getString(R.string.html_loop),

forecast.getDay(),forecast.getLowTemp(),forecast.getHighTemp(),

forecast.getIcon());

                bufResult.append(tempLine);

}

bufResult.append(getResources().getString(R.string.html_foot));

browser.loadDataWithBaseURL(null, bufResult.toString(), "text/html", "UTF-8", null);

Noter l’absence de l’appel aux méthodes fromHtml et toHtml qui ne nous ont pas paru efficientes.

1.1.2       Les images

Les formats acceptés sont PNG et JPEG (GIF est non recommandé).

Il suffit de déposer les fichiers images dans res\drawable pour qu’elles soient référencées par le système.

Ainsi l’image res/drawable/myimage.png peut être utilisée

soit dans le code :

Resources res = getResources();

Drawable drawable = res.getDrawable(R.drawable.myimage);

 

soit dans la description des IHM dans le fichier de layouts :

<ImageView

    android:layout_height="wrap_content"

    android:layout_width="wrap_content"

    android:src="@drawable/myimage" />

1.1.2.1       Exemple d’utilisation de ressources

Cet exemple possède quatre fichiers, le code, le fichier des chaînes de caractères, le fichier de layout et enfin un fichier selector qui n’est là que pour le plaisir et qui choisit une image en fonction de l’état de l’objet sur lequel il est.

Cet exemple montre des TextView, une scroll et deux ImageButton instanciés soit directement dans leur déclaration xml soit dans le code Java.

Le fichier de strings (res\values\strings.xml) :

<?xml version="1.0" encoding="utf-8"?>

<resources>

                <!--Simple string-->

                <string name="hello">Hello World, ResourcesTuto!</string>

                <string name="app_name">ResourcesTuto</string>

                <string name="simpleString">Good Morning My name is Bond, James Bond</string>

                <!--String with parameters ($s for string, $d for decimal (any decimal:integer, double, float...)-->

                <string name="param_message">Hello, i love %1$s, i hate %2$s, i am %3$d years old</string>

                <string name="spinner">Do you like: </string>

                <!--String array-->

                <string-array name="i_like_array">

                               <item>chocolate</item>

                               <item>television</item>

                               <item>internet</item>

                               <item>nicotine</item>

                               <item>hug</item>

                               <item>Santa Claus</item>

                </string-array>

                <!--Plurals String-->

                <plurals name="singleOrPlural">

                               <item quantity="one">I am alone on moon</item>

                               <item quantity="other">I am 6 900 000 000 on earth</item>

                </plurals>

                <string name="button">An image on the button</string>

</resources>

Le fichier de déclaration des layouts :

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:orientation="vertical"

    android:layout_width="fill_parent"

    android:layout_height="fill_parent"

    >

<TextView

                android:id="@+id/simpleStringTV" 

    android:layout_width="fill_parent"

    android:layout_height="wrap_content"

    android:text="@string/hello"

    />

<TextView

                android:id="@+id/simpleStringToSetTV" 

    android:layout_width="fill_parent"

    android:layout_height="wrap_content"

    />

<TextView

                android:id="@+id/parameterStringTV" 

    android:layout_width="fill_parent"

    android:layout_height="wrap_content"

    />

<TextView

                android:id="@+id/pluralsTV" 

    android:layout_width="fill_parent"

    android:layout_height="wrap_content"

    />

<Spinner

                android:id="@+id/Spinner01"

                android:layout_width="wrap_content"

                android:layout_height="wrap_content"

                android:prompt="@string/spinner"

                />

<ImageButton

                android:id="@+id/ImageButtonXmlInstanciatedIB"

                android:layout_width="wrap_content"

                android:layout_height="wrap_content"

                android:src="@drawable/icon"/>

<ImageButton

                android:id="@+id/ImageButtonToInstanciateIB"

                android:layout_width="wrap_content"

                android:layout_height="wrap_content"/>  

</LinearLayout>

Le fichier java :

public class ResourcesTuto extends Activity {

    /** Called when the activity is first created. */

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        //Instanciate the resources object:

        Resources resources=getResources();

       

        //Then instanciate the textView @+id/simpleStringToSetTV

        TextView textViewSimple=(TextView)findViewById(R.id.simpleStringToSetTV);

        textViewSimple.setText(getString(R.string.simpleString));

       

        //Then instanciate a string with paremeter

        String parameterString=String.format(getString(R.string.param_message),"peace","war",2);

        //And set the text view

        TextView textViewWithParam=(TextView)findViewById(R.id.parameterStringTV);

        textViewWithParam.setText(parameterString);

       

        //Then instanciate a string with plurals or not

        int number=0;

        if(Math.random()<0.5d){

                number=1;

        }else{

                number=2;

        }

        String pluralsString=resources.getQuantityString(R.plurals.singleOrPlural, number);

        //And its associated textView

        TextView textViewPlurals=(TextView)findViewById(R.id.pluralsTV);

        textViewPlurals.setText(pluralsString);

        String[] planets = resources.getStringArray(R.array.i_like_array);

 

        //Then instanciate the spinner using the array string

        Spinner spinner = (Spinner) findViewById(R.id.Spinner01);

        ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(

                this, R.array.i_like_array, android.R.layout.simple_spinner_item);

        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

        spinner.setAdapter(adapter);

       

        //Then find a ressource for the ImageButton

        Drawable drawableBck=resources.getDrawable(R.drawable.selector);

        Drawable drawable=resources.getDrawable(R.drawable.icon);

        //And set it:

        ImageButton imageButton=(ImageButton)findViewById(R.id.ImageButtonToInstanciateIB);

        imageButton.setBackgroundDrawable(drawableBck);

        imageButton.setImageDrawable(drawable);

 

    }

}

Et enfin le fichier de sélection d’une image (res\drawable\selector.xml) :

<?xml version="1.0" encoding="UTF-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android">

                <!-- when focused -->

                <item android:state_focused="true" android:drawable="@drawable/sky"/>

                <!-- when pressed -->

                <item android:state_pressed="true" android:drawable="@drawable/coffee" />

                <!-- when normal -->

                <item android:drawable="@drawable/icon" />

</selector>

Vous remarquerez qu’en fonction de l’état, le fichier pointe sur une ressource différente. Ces ressources sont des fichiers png stockés sous res\drawable et nommés coffee.png et sky.png. Il est à remarquer que les états pour un ImageButton quand il a le focus passent de focused à normal puis à pressed. Les valeurs possibles pour un tel fichier sont :

<selector xmlns:android="http://schemas.android.com/apk/res/android"

    android:constantSize=["true" | "false"]

    android:dither=["true" | "false"]

    android:variablePadding=["true" | "false"] >

    <item

        android:drawable="@[package:]drawable/drawable_resource"

        android:state_pressed=["true" | "false"]

        android:state_focused=["true" | "false"]

        android:state_selected=["true" | "false"]

        android:state_checkable=["true" | "false"]

        android:state_checked=["true" | "false"]

        android:state_enabled=["true" | "false"]

        android:state_window_focused=["true" | "false"] />

</selector>

 

1.1.2.2       Au de là

http://developer.android.com/guide/topics/resources/drawable-resource.html

En fait il existe 9 types d’images différentes pour Android :

·         Bitmap File qui sont les fichiers .png, .jpeg et .gif présentés ci-dessus.

·         Nine-Patch file qui est un fichier png avec des régions étirables pour permettre le redimensionnement de l’image.

·         Layer List qui est un tableau d’image.

·         StateList qui est un fichier xml qui référence  des images pour des états différents (typiquement pour un bouton qui possède plusieurs états focus, pressed, released). Nous venons de le voir au sein du fichier selector.xml.

·         LevelList qui est un fichier xml qui référence  des images pour des niveaux différents, chaque image est associée une valeur numérique maximale.

·         TransitionDrawable qui est un fichier xml qui référence  deux images qui morphe de l’une à l’autre

·         ClipDrawable qui est un fichier xml qui décrit une image qui se dévoilera petit à petit (via du code)

·         ScaleDrawable  qui est un fichier xml qui décrit une image qui pourra être zoomée.

·         ShapeDrawable qui est un fichier xml qui décrit une image par de la géométrie (ovale, rectangle…) C’est une image dessinée de manière informatique comme un graphique 2D.

Enfin, pour aller encore plus loin, il faut aller voir 2D Graphics :

http://developer.android.com/guide/topics/graphics/2d-graphics.html

1.1.3       Les fichiers XML

Ils se trouvent sous res\xml\ et on y accède par getResources().getXml(R.xml.nomDuFichier) qui retourne un XmlPullParser sur le fichier.

try {

                XmlPullParser xpp = getResources().getXml(R.xml.words);

 

                while (xpp.getEventType() != XmlPullParser.END_DOCUMENT) {

                               if (xpp.getEventType() == XmlPullParser.START_TAG) {

                                               if (xpp.getName().equals("word")) {

                                                               items.add(xpp.getAttributeValue(0));

                                               }

                               }

                               xpp.next();

                }

} catch (Throwable t) {

                Toast.makeText(this, "Request failed: " + t.toString(), 4000).show();

}

Le XmlPullParser est assez simpliste comme parseur, il ne connait que les méthodes :

·         next qui renvoie la ligne suivante

·         getName qui renvoie le nom de la balise

·         getValue qui renvoie la valeur contenue au sein de la balise

·         et getEventType qui renvoie :

o   End_Document quand le document est fini

o   Start_Tag quand il rencontre une balise ouvrante

o   End_Tag quand il en rencontre une fermante.

Autant dire que pour la manipulation de fichier Xml il vaut mieux rester humble.

1.1.4       Les couleurs

Elles se placent sous res\values et peuvent se nommer color.xml :

<resources>

   <color name="opaque_red">#f00</color>

   <color name="translucent_red">#80ff0000</color>

</resources>

Chaque couleur est décrite par son nom et son code RGB hexadécimal.

Dans le code, elles s’utilisent comme ça :

Resources res = getResources();

int color = res.getColor(R.color.opaque_red);

Dans un fichier de ressources :

<TextView

    android:layout_width="fill_parent"

    android:layout_height="wrap_content"

    android:textColor="@color/translucent_red"

    android:text="Hello"/>

1.1.5       Les Styles et thèmes

La convention souhaite que ce fichier soit enregistré sous res\values\styles.xml, il peut alors être référencé dans votre application par la balise @style/MonStyle.

Les styles et les thèmes d’une application peuvent être surchargés ou définis :

<?xml version="1.0" encoding="utf-8"?>

<resources>

  <style name="ToDoTheme" parent="@android:style/Theme.Black">

    <item name="android:textSize">12sp</item>

  </style>

</resources>

Ou bien :

<?xml version="1.0" encoding="utf-8"?>

<resources>

  <style name="MyTheme">

    <item name="android:textSize">12sp</item>

    <item name="android:textColor">#111</item>

  </style>

  <style name="MySubTheme" parent="MyTheme” >

    <item name="android:textSize">8sp</item>

  </style>

 

</resources>

Et il sera utilisé dans le descripteur de ressources (le layout.xml) de la manière suivante :

<TextView

    style="@style/CodeFont"

    android:text="@string/hello" />

De plus, un style peut être appliqué à un ensemble de composant ayant spécifié un inputType, ou plus exactement la notion d’inputType peut être incluse dans le style. Ainsi au lieu d’écrire :

<EditText

    android:inputType="number"

... />

On écrira un style spécifique pour les éléments graphiques de type inputType=number:

<style name="Numbers">

  <item name="android:inputType">number</item>

  ...

</style>

Que l’on associera à cet élément (et à tous ceux qui sont de inputType number) :

<EditText

    style="@style/Numbers"

    ... />

Lorsque l’on affecte à l’ensemble de l’activité ou de l’application un style, cela s’appelle un thème. Ce thème se spécifie dans le fichier manifest.xml (et non plus dans le layout.xml).

Pour l’application :

<application android:theme="@style/CustomTheme">

 

Pour une activité de l’application :

 

<activity android:theme="@android:style/Theme.Dialog">

Cela marche de manière identique que les styles, on peut les spécifier et déclarer les nôtres (on les appelle par @style/MonTheme)

<style name="CustomDialogTheme" parent="@android:style/Theme.Dialog">

    <item name="android:windowBackground">@drawable/custom_dialog_background</item>

</style>

Et on l’applique :

<activity android:theme="@style/CustomDialogTheme">

La liste des styles Android se trouve :

http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=core/res/res/values/styles.xml;h=d7b654e49809cb97a35682754b1394af5c8bc88b;hb=HEAD

Et celles des thèmes sous :

http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=core/res/res/values/themes.xml;h=6b3d7407d1c895a3c297e60d5beac98e2d34c271;hb=HEAD

Pour utiliser ces thèmes et ces styles et les surcharger, il suffit de remplacer l’underscore par un point : "@android:style/Theme.NoTitleBar".

 

1.1.6       La localisation des ressources

Où comment Android décide quel est le fichier de ressources à utiliser (cela se comprend bien dans le cas de l’internationalisation mais ce concept est étendu à l’arbre des devices Android). Cette liste de paramètres pris en compte par Android est la suivante :

·         Code réseau et code mobile du pays (le pays et le réseau de la carte SIM) dit code MCC (Mobile Country Code) et MNC (Mobile Network Code). http://en.wikipedia.org/wiki/Mobile_Network_Code

·         Langue et région (internationalisation usuelle)

·         Taille de l’écran (small, medium et large)

·         Le rapport Largeur/longueur (long ou not long) de l’écran

·         L’orientation de l’écran (port pour portrait, land pour landscape ou square pour carré)

·         Densité de l’écran en pixel (ldpi pour low dpi, mdpi pour medium dpi et hdpi pour high dpi)

·         Type d’écran tactile (notouch pour non sensitif, stylus pour le stylet et finger pour sensitif)

·         Visibilité du clavier (keyexposed pour clavier physique apparent, keyshidden pour clavier physique caché ou keysoft pour un clavier logiciel)

·         Type de clavier (nokeys, qwerty ou 12Key ???)

·         Type de navigation (nonav pour pas de navigation, dpad pour le pad, trackball pour trackball et wheel pour la roue de la souris ???)

Ainsi pour définir la branche dans laquelle se trouve votre ressource, il faut spécifier son chemin :

·         Il faut définir dans l’ordre des paramètres ci-dessus listés,

·         On peut omettre des éléments,

·         Il faut un unique qualificateur par type de paramètre,

·         Il faut les séparer par un trait d’union.

Ainsi mcc234-mcn20-en-rUK-small-long-port-ldpi-finger-keysoft-qwerty-dpad spécifie le chemin pour le royaume uni, pour l’opérateur « Hutchison 3G UK Ltd », en langue anglaise pour le royaume uni, pour un petit portable au format 16/9, en mode portrait, avec peu de densité de point par inch, qui est sensitif au toucher, avec un clavier logiciel de type qwerty dont la navigation se fait au pad.

Maintenant que nous avons bien conscience du délire complet d’établir une application Android pour l’arbre complet des choix, il faut comprendre la stratégie d’Android pour choisir quelles ressources il va utiliser et comment minimiser le nombre de ressources spécifiques que nous allons implémenter.

Android choisira le dossier pour lesquels le nombre de qualificateurs qui correspondent le plus avec ceux du portable utilisateur et en cas de doute Android choisira celui pour laquelle la locale est identique. Cela permet une épuration drastique de cet arbre : n’utiliser que ce qui vous paraît vraiment pertinent. Ensuite si Android ne trouve pas de correspondance, il enverra une exception, donc il faut toujours définir pour toutes les ressources le dossier par défaut, celui qui ne contient pas de qualificateur. Donc toujours avoir un dossier res\values\, res\drawable, et de même pour vos ressources layout, anim, raw, xml et tutti quanti.

Lorsqu’Android détecte un changement sur le matériel de l’une de ces valeurs, il détruit et reconstruit l’application. Pour tempérer ce comportement, il faut rajouter dans la description de votre activité la ligne android :configChanges= « orientation|keyboardhidden ». Cela supprime ce comportement pour les paramètres spécifiés et déclenche la méthode onConfigurationChanged de votre activité. Les valeurs possibles sont orientation, keyboardhidden, fontscale, locale, keyboard, touchscreen et navigation. Dans le code, il suffit dans la méthode OnConfigChanged d’évaluer _newConfig.orientation pour retrouver la nouvelle orientation et associer le traitement qui s’ensuit, de même pour les autres paramètres.

Les consignes d’Android concernant ce sujet sont :

·         Concevez votre application pour qu’elle marche pour toutes les locales (res\values doit toujours exister)

·         Concevez avec des layout flexible qui s’adapte à la taille de l’écran, pas de taille en dur.

·         Ne pas créez plus de ressources que nécessaire

·         Utilisez le contexte Android pour avoir un « look-up » manuel : context.getResources().getConfiguration().locale.getDisplayName();

·         Testez vos applications avec différentes configurations

·         Définissez la liste des paramètres que vous prenez en compte, gravez la dans le marbre, testez toutes ces configurations et publiez cette liste quand vous déposez votre application sur l’Android market