Introduction▲
De toutes les particularités d'Android vis-à-vis des autres systèmes d'exploitation mobiles, une me plait tout particulièrement : la possibilité de créer des interfaces graphiques à l'aide de simples fichiers XML. Cette particularité favorise grandement la clarté du code source des applications. En effet, séparer la description de l'interface graphique (layout XML) de la logique applicative (code Java) est en gage d'extensibilité.
Etant très attaché aux technologies du Web (XHTML, JavaScript, CSS, etc.), j'ai souvent tendance à comparer la création d'applications au développement d'un site Internet.
- les layouts s'apparentent au contenu XHTML ;
- le code applicatif à la logique JavaScript.
Les aficionados du développement Web auront probablement remarqué qu'une composante est absente du listing précédent : la présentation, généralement décrite via une feuille de style CSS. L'utilisation du CSS permet ainsi de définir un ensemble de styles applicables aux différentes balises de l'interface graphique. Android inclut un système proche des CSS : les styles et les thèmes.
Souvent sous-utilisés, les styles et thèmes Android permettent de grandement factoriser les styles et donc de conserver une cohérence à travers une application. C'est une des raisons pour lesquelles GreenDroid utilise largement ces possibilités. Nous allons donc en faire une rapide présentation dans les lignes qui suivent.
Note :
L'ensemble des démos de cet article sont disponibles dans cette
archive.
Cette dernière ne contient qu'un unique projet avec lequel il vous
faudra "jouer" pour appliquer telle ou telle méthode de style.
N'hésitez pas à laisser un commentaire si vous n'arrivez vraiment
pas à obtenir un résultat fonctionnel !
Appliquer un style à une balise▲
Prenons le code suivant :
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns
:
android
=
"http://schemas.android.com/apk/res/android"
android
:
layout_width
=
"fill_parent"
android
:
layout_height
=
"fill_parent"
android
:
gravity
=
"center"
android
:
text
=
"Bonjour !!!"
/>
Ce code n'a rien de compliqué et est même plutôt "classique". Par défaut Android oblige le développeur à définir une hauteur et une largeur pour chacune des vues. C'est ce qui nous oblige à souvent ajouter du code du style android:layout_[width|height]="[wrap_content|fill_parent]". Il faut avouer que ça peut devenir assez rébarbatif. Il est néanmoins possible de réduire le code de la façon suivante :
<TextView
xmlns
:
android
=
"http://schemas.android.com/apk/res/android"
style
=
"@style/FillParent"
android
:
gravity
=
"center"
android
:
text
=
"Bonjour !!!"
/>
Note : L'attribut XML style n'appartenant pas au namespace android, il n'est pas nécessaire de le préfixer par android:.
Pour ce faire, il suffit de créer un style FillParent dans les styles du projet :
<style
name
=
"FillParent"
>
<item
name
=
"android:layout_width"
>
fill_parent</item>
<item
name
=
"android:layout_height"
>
fill_parent</item>
</style>
Note : On utilise généralement le fichier res/values/styles.xml pour définir l'ensemble des styles d'un projet. Sachez néanmoins que cela n'a rien d'obligatoire. La seule nécessité est de définir le style dans la balise <resources /> dans un fichier présent dans res/values.
Notion d'héritage▲
Contrairement aux feuilles de style CSS, les styles Android ne fonctionnent pas en cascade. Ainsi une balise <TextView> contenue dans un <LinearLayout> disposant d'un style @style/MyStyle n'hérite aucunement des propriétés de son parent. Il est néanmoins possible de faire hériter un style d'un autre style. Si nous souhaitons maintenant créer un style forçant la vue à prendre la taille de son parent et à centrer son contenu nous pouvons définir :
<style
name
=
"FillParent.Centered"
parent
=
"@style/FillParent"
>
<item
name
=
"android:gravity"
>
center</item>
</style>
Il est également possible de simplifier cette déclaration de la façon suivante :
<style
name
=
"FillParent.Centered"
>
<item
name
=
"android:gravity"
>
center</item>
</style>
Lorsque le nom d'un style est du format <Part1>.<Part2>, Android considère automatiquement que le parent est le style de nom @style/Part1.
En utilisant ce nouveau style, il est possible de simplifier notre layout exemple :
<TextView
style
=
"@style/FillParent.Centered"
android
:
text
=
"Bonjour !!!"
/>
Les thèmes▲
Les thèmes Android ne sont rien d'autre que des styles s'appliquant à l'ensemble des balises de vos layouts. Pour contrer le problème de android:layout_[width|height] obligatoires, on peut donc, par exemple, imaginer un thème initialisant l'ensemble de ces valeurs à fill_parent. On commence donc par créer un style nommé Theme.Custom héritant du thème Android Theme.Black. Traditionnellement, bien qu'un style soit un thème, on le place dans un fichier nommé res/values/themes.xml.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style
name
=
"Theme.Custom"
parent
=
"@android:style/Theme.Black"
>
<item
name
=
"android:layout_height"
>
fill_parent</item>
<item
name
=
"android:layout_width"
>
fill_parent</item>
</style>
</resources>
On applique ensuite ce thème à l'ensemble de l'application en ajoutant l'attribut XML android:theme="@style/Theme.Custom" à la balise <application /> de notre AndroidManifest.xml. Le code du layout est donc de la forme suivante :
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns
:
android
=
"http://schemas.android.com/apk/res/android"
android
:
gravity
=
"center"
android
:
text
=
"Bonjour !"
/>
Les attributs de style▲
Utilisés tels quels, les thèmes n'ont que peu d'intérêt. En effet, appliquer une valeur par défaut à l'ensemble des balises de votre application est quelque peu hasardeux et inutile. Les thèmes prennent tout leur sens lorsqu'ils sont utilisés en conjonction avec les attributs de style.
Un attribut de style peut-être utilisé comme "indirection de style". Plutôt que de longs discours, appliquons un attribut de style à notre <TextView />. On commence par créer un nouvel attribut nommé myTextViewAttr dans le fichier res/values/attrs.xml :
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr
name
=
"myTextViewAttr"
/>
</resources>
Il ne nous reste plus maintenant qu'à appliquer ce style au layout voulu :
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns
:
android
=
"http://schemas.android.com/apk/res/android"
style
=
"?attr/myTextViewAttr"
android
:
text
=
"Bonjour !"
/>
La <TextView /> précédente dispose de l'attribut de style ?attr/myTextViewAttr. On notera bien la présence d'un caractère "?" en lieu et place du traditionnel "@". L'utilisation du point d'interrogation permet d'informer le système que la ressource de style recherchée n'est pas myTextViewAttr (nous aurions alors écrit @style/myTextViewAttr) mais un style "pointé" par l'attribut myTextViewAttr.
Si on lance notre application dans l'état, nous obtenons une erreur affirmant qu'aucun des attributs android:layout_[height|width] n'a été initialisé. Ce résultat est tout à fait logique car le style qui se cache derrière l'attribut myTextViewAttr n'a pas été défini. Pour le définir, il suffit de l'initialiser dans le thème de notre application !
<style
name
=
"Theme.Custom_2"
parent
=
"@android:style/Theme.Black"
>
<item
name
=
"myTextViewAttr"
>
@style/FillParent.Centered</item>
</style>
L'application est maintenant parfaitement fonctionnelle et applique le style @style/FillParent.Centered à l'ensemble des balises disposant de l'attribut de style ?attr/myTextViewAttr. Simple et puissant n'est-ce pas ?
Etendre des thèmes Android▲
L'ensemble des fonctionnalités décrites dans cet article sont accessibles au développeur aussi bien pour créer ses propres styles que pour étendre et modifier les thèmes existants d'Android. Ainsi, lorsque vous souhaitez développer une application à dominante "noir sur blanc", il suffit de créer un thème héritant de @android:style/Theme.Light et de modifier les attributs à votre convenance. Voici un exemple de thème Android supprimant la fameuse ombre en haut de l'écran et modifiant le style par défaut de la titlebar :
<style
name
=
"Theme.Custom_3"
parent
=
"@style/Theme.Custom_2"
>
<item
name
=
"android:windowContentOverlay"
>
@null</item>
<item
name
=
"android:windowTitleStyle"
>
@style/WindowTitle</item>
<item
name
=
"android:windowTitleBackgroundStyle"
>
@style/WindowTitleBackground</item>
</style>
Les styles associés sont définis de la façon suivante :
<style
name
=
"WindowTitleBackground"
>
<item
name
=
"android:background"
>
@drawable/title_bar</item>
</style>
<style
name
=
"WindowTitle"
parent
=
"@android:style/WindowTitle"
>
<item
name
=
"android:textAppearance"
>
@style/TextAppearance.WindowTitle</item>
</style>
<style
name
=
"TextAppearance.WindowTitle"
parent
=
"@android:style/TextAppearance.WindowTitle"
>
<item
name
=
"android:textStyle"
>
bold|italic</item>
</style>
Le résultat est quelque peu coloré !
A mon grand regret, la documentation sur les différents attributs de style d'Android est assez pauvre. Jetez donc un oeil à la documentation de R.attr et n'hésitez surtout pas à regarder le code source de la plateforme pour connaître l'utilité de tel ou tel attribut. Stylez/Thèmez bien !
Remerciements▲
Un grand merci à ClaudeLELOUP pour sa relecture.