Android,
A Complete Course, From
Basics to Enterprise Edition
Android, A Complete Course, From Basics to Enterprise
Edition
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 :
La
propriété intellectuelle du code appartient à :
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
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/.
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.
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>
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 < en lieu et place du <.
Un exemple, dans votre fichier string.xml :
<string name="html_head"><html><body><table><tr><th width=\"50%\">Jour</th><th>Basse</th><th>Haute</th><th>Tendance</th></tr></string>
<string name="html_loop"><tr><td align=\"center\">%1$s</td><td
align=\"center\">%2$s</td></td><td align=\"center\">%3$s</td><td><img
src=\"%4$s\"></td></tr></string>
<string name="html_foot"></table></body></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.
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"
/>
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>
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
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.
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"/>
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 :
Et celles des thèmes sous :
Pour utiliser ces thèmes et ces styles et les
surcharger, il suffit de remplacer l’underscore par un point : "
@android:style/Theme.NoTitleBar
"
.
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