Amis, super tout le vendredi. Nous voulons partager avec vous une traduction d'un article préparé spécialement pour les étudiants du cours
«Android-développeur. Cours avancé .
" Bonne lecture.

Comment styliser de manière déclarative du texte sur Android.
Illustration de Virginia PoltrackTextView
dans les applications Android fournit plusieurs attributs pour styliser le texte et diverses façons de les appliquer. Ces attributs peuvent être définis directement dans la présentation, appliquer un style à la vue ou au thème de la présentation ou, si vous le souhaitez, définir l'apparence du texte. Mais qu'est-ce qui devrait être utilisé? Et que se passe-t-il si vous les combinez?
Quoi et quand l'utiliser?Cet article décrit différentes approches de la stylisation déclarative du texte (c'est-à-dire lorsque vous définissez des styles dans un fichier XML), traite de la portée et de la priorité de chaque méthode.
tl; dr;
Vous
devriez lire l'intégralité du message, mais voici un résumé.
Rappelez-vous l'ordre de priorité des différentes méthodes de style - si vous essayez de styliser du texte et que vous ne voyez pas les résultats attendus, alors vos modifications sont probablement remplacées par quelque chose avec une priorité plus élevée dans cette hiérarchie:
Hiérarchie des méthodes de style de texteJe suggère la procédure suivante pour le style:
- Définissez n'importe quel style d'application dans
textViewStyle
comme style par défaut pour votre thème. - Installez le (petit) ensemble de
TextAppearance
que votre application utilisera (ou utilisez / héritez des styles MaterialComponent), et référencez-les directement à partir de votre vue - Créez un
style
en définissant des attributs non pris en charge par TextAppearance
(qui détermineront eux-mêmes l'un de vos TextAppearance
). - Effectuez tout style unique directement dans la mise en page.
Montrez du style
Vous pouvez définir directement les attributs
TextView
dans la mise en page, mais cette approche peut être plus fastidieuse et dangereuse. Imaginez que de cette manière, vous essayez de mettre à jour la couleur de tous les TextViews dans l'application. Comme pour toutes les autres vues, vous pouvez (et devriez!) Utiliser des styles à la place pour garantir la cohérence, la réutilisation et la facilité de mise à jour. À cette fin, je recommande de créer des styles de texte chaque fois que vous souhaitez probablement appliquer le même style à plusieurs vues. Ceci est extrêmement simple et largement pris en charge par le système de vue Android.
Que se passe-t-il sous le capot lorsque vous modélisez la vue? Si vous avez déjà écrit votre vue personnalisée, vous avez probablement vu l'appel à
context.obtainStyledAttributes (AttributeSet, int [], int, int) . Ainsi, le système de vue dans Android passe à la vue les attributs spécifiés dans la mise en page. Le paramètre
AttributeSet
, en fait, peut être considéré comme une carte des paramètres XML que vous spécifiez dans votre mise en page. Si AttributeSet définit le style, le
style est lu en premier , puis les attributs spécifiés directement dans la vue lui sont appliqués. Ainsi, nous arrivons à la première règle de priorité.
Affichage → StyleLes attributs définis directement dans la vue prévalent toujours et remplacent les attributs définis dans le style. Notez qu'une
combinaison d'attributs de style et de vue est appliquée; la définition d'un attribut en vue, qui est également spécifié dans le style,
n'annule pas tout le style. Il convient également de noter qu'à votre avis, il n'existe aucun moyen réel de déterminer d'où vient la stylisation; Ceci est décidé par le système de visualisation pour vous une fois dans un appel similaire. Vous ne pouvez pas obtenir les deux options et choisir.
Bien que les styles soient extrêmement utiles, ils ont leurs limites. L'un d'eux est que vous ne pouvez appliquer qu'un seul style à la vue (par opposition à quelque chose comme CSS, où vous pouvez appliquer plusieurs classes).
TextView
, cependant, a une astuce: il fournit l'attribut
TextAppearance
, qui fonctionne de manière similaire au
style
. Si vous
TextAppearance
texte via
TextAppearance
, laissez l'attribut
style
libre pour les autres styles, ce qui semble pratique. Examinons de plus près ce qu'est
TextAppearance
et comment cela fonctionne.
Apparence du texte
Il n'y a rien de magique dans
TextAppearance
(par exemple, un mode secret pour appliquer plusieurs styles que vous ne devriez pas connaître !!!!),
TextView
vous évite un travail inutile. Jetons un coup d'œil au constructeur
TextView
pour comprendre ce qui se passe.
TypedArray a = theme.obtainStyledAttributes(attrs, com.android.internal.R.styleable.TextViewAppearance, defStyleAttr, defStyleRes); TypedArray appearance = null; int ap = a.getResourceId(com.android.internal.R.styleable.TextViewAppearance_textAppearance, -1); a.recycle(); if (ap != -1) { appearance = theme.obtainStyledAttributes(ap, com.android.internal.R.styleable.TextAppearance); } if (appearance != null) { readTextAppearance(context, appearance, attributes, false); appearance.recycle(); }
Alors qu'est-ce qui se passe ici? Essentiellement,
TextView
regarde d'abord pour voir si vous avez spécifié
android:textAppearance
, si c'est le cas, il charge ce style et applique toutes les propriétés qui y sont répertoriées. Plus tard, il charge tous les attributs de la vue (dont il se souvient, y compris le style) et les applique. Nous arrivons donc à la deuxième règle de priorité:
Affichage → Style → Apparence du texteÉtant donné que l'apparence du texte est d'abord vérifiée, tous les attributs définis directement dans la vue ou dans le style la remplaceront.
Avec
TextAppearance
, il y a une autre
TextAppearance
en
TextAppearance
à garder à l'esprit: il prend en charge un
sous -
ensemble des attributs de style
TextView
par
TextView
. Pour mieux comprendre ce que je veux dire, revenons à cette ligne:
obtainStyledAttributes(ap, android.R.styleable.TextAppearance);
Nous avons examiné la version
receiveStyledAttributes
avec 4 arguments, cette version à 2 arguments est légèrement différente de celle-ci. Elle examine le style donné (tel que défini par le premier
id
paramètre) et le filtre uniquement en fonction des attributs du style qui apparaissent dans le deuxième paramètre, le tableau
attrs
. So styleable
android.R.styleable.TextAppearance
définit la portée de
TextAppearance
. En regardant cette définition, nous voyons que
TextAppearance
prend en charge de nombreux attributs,
mais pas tous, TextView
charge par TextView
.
<attr name="textColor" /> <attr name="textSize" /> <attr name="textStyle" /> <attr name="typeface" /> <attr name="fontFamily" /> <attr name="textColorHighlight" /> <attr name="textColorHint" /> <attr name="textColorLink" /> <attr name="textAllCaps" format="boolean" /> <attr name="shadowColor" format="color" /> <attr name="shadowDx" format="float" /> <attr name="shadowDy" format="float" /> <attr name="shadowRadius" format="float" /> <attr name="elegantTextHeight" format="boolean" /> <attr name="letterSpacing" format="float" /> <attr name="fontFeatureSettings" format="string" />
Attributs de style pris en charge par TextAppearance
Voici quelques attributs
TextView
qui ne sont pas inclus dans
TextAppearance
:
lineHeight[Multiplier|Extra]
,
lines
,
breakStrategy
et
breakStrategy
.
TextAppearance
fonctionne au niveau des caractères, pas au niveau des paragraphes, donc les attributs qui affectent la mise en page entière ne sont pas pris en charge.
Par conséquent,
TextAppearance
très utile, il nous permet de définir un style orienté vers les attributs de stylisation du texte et laisse le
style
visible à d'autres fins. Cependant, il a une portée limitée et se situe au bas de la chaîne de priorité, alors n'oubliez pas les limitations.
Valeurs par défaut raisonnables
Lorsque nous avons examiné la façon dont la vue Android résout les attributs (
context.obtainStyledAttributes
), nous l'avons en fait un peu simplifié. Elle appelle
theme.obtainStyledAttributes (en utilisant le contexte de
Theme Context
actuel 'a). Lors de la vérification du
lien , l'ordre de priorité que nous avons examiné précédemment est affiché, et 2 autres emplacements sont indiqués qu'il recherche pour résoudre les attributs: le style par défaut pour la vue et le thème.
Lors de la détermination de la valeur finale d'un attribut particulier, quatre paramètres d'entrée entrent en jeu:
- Toutes les valeurs d'attribut dans cet AttributeSet.
- La ressource de style spécifiée dans AttributeSet (nommée "style").
- Le style par défaut spécifié par defStyleAttr et defstyleres
- Valeurs de base dans ce fil.
Ordre de priorité de style dans la documentation du thèmeNous reviendrons sur les thèmes, mais examinons d'abord les styles par défaut. Quel est ce style par défaut? Pour répondre à cette question, je pense qu'il serait utile de prendre une petite sortie du thème
TextView
et de jeter un œil à un simple
Button
. Lorsque vous insérez
<
Button
>
dans votre mise en page, cela ressemble à ceci.
Bouton standardPourquoi? Si vous regardez le code source de
Button
, vous verrez qu'il est plutôt maigre:
public class Button extends TextView { public Button(Context context) { this(context, null); } public Button(Context context, AttributeSet attrs) { this(context, attrs, com.android.internal.R.attr.buttonStyle); } public Button(Context context, AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } public Button(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override public CharSequence getAccessibilityClassName() { return Button.class.getName(); } @Override public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) { if (getPointerIcon() == null && isClickable() && isEnabled()) { return PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_HAND); } return super.onResolvePointerIcon(event, pointerIndex); } }
C'est tout! Voici toute la classe (pas de commentaire). Vous pouvez le vérifier vous-même
ici . J'attendrai. D'où viennent l'arrière-plan, les majuscules, l'ondulation, etc.? Vous avez peut-être manqué, mais tout sera défini dans le constructeur avec 2 arguments; celui qui est appelé lorsque la mise en page est extraite de XML. Il s'agit du dernier paramètre qui définit
defaultStyleAttr
dans
com.android.internal.R.attr.buttonStyle
. Il s'agit du style par défaut, qui est essentiellement un point de référence indirect, vous permettant de spécifier le style par défaut. Il ne pointe pas directement vers le style, mais vous permet de pointer vers l'un de ceux spécifiés dans votre rubrique qu'il vérifiera lors de la résolution des attributs. Et c'est exactement ce que font tous les thèmes dont vous héritez habituellement pour donner l'apparence des widgets standard. Par exemple, si vous regardez la rubrique Material, elle définit
@style/Widget.Material.Light.Button
, et c'est ce style qui fournit tous les attributs que
theme.obtainStyledAttributes
si vous n'avez rien spécifié d'autre.
De retour à
TextView
, il propose également un style par défaut:
textViewStyle
. Cela peut être très pratique si vous souhaitez appliquer certains styles à chaque TextView de votre application. Supposons que vous souhaitiez définir l'interligne par défaut à 1,2. Vous pouvez le faire avec
style/TextAppearance
et essayer de l'appliquer lors d'une révision de code (ou peut-être même avec une élégante règle personnalisée dans Lint), mais vous devez être vigilant et vous assurer de recruter de nouveaux membres de l'équipe , soyez prudent avec le refactoring, etc.
Une meilleure approche serait de spécifier votre propre style par défaut pour tous les
TextView
de l'application, en définissant le comportement souhaité. Vous pouvez le faire en définissant votre propre style pour
textViewStyle
, qui est hérité de la plate-forme ou de
MaterialComponents/AppCompat
par défaut.
<style name="Theme.MyApp" parent="@style/Theme.MaterialComponents.Light"> ... <item name="android:textViewStyle">@style/Widget.MyApp.TextView</item></style> <style name="Widget.MyApp.TextView" parent="@android:style/Widget.Material.TextView"> <item name="android:textAppearance">@style/TextAppearance.MyApp.Body</item> <item name="android:lineSpacingMultiplier">@dimen/text_line_spacing</item> </style>
Dans cette optique, notre règle de priorité prend la forme:
Affichage -> Style -> Style par défaut -> Apparence du texteDans le cadre de la résolution des attributs du système de vue, cet emplacement est rempli après les styles (de sorte que tout dans le style est annulé par le style appliqué ou les attributs de vue par défaut), mais remplacera toujours l'apparence du texte. Les styles par défaut peuvent être très pratiques. Si vous décidez d'écrire votre propre vue personnalisée, elles peuvent être un moyen puissant d'implémenter un comportement par défaut, ce qui facilite la personnalisation.
Si vous héritez du widget et ne spécifiez pas votre propre style par défaut, assurez-vous d'utiliser le style de classe parent par défaut dans les constructeurs (ne passez pas seulement 0). Par exemple, si vous
AppCompatTextView
d'
AppCompatTextView
et écrivez votre propre constructeur avec 2 arguments, assurez-vous de passer
android.R.attr.textViewStyle defaultStyleAttr
(
comme ici ), sinon vous perdrez le comportement de la classe parent.
Thèmes
Comme mentionné précédemment, il existe un autre (dernier, je le promets) moyen de fournir des informations de style. Un autre endroit
theme.obtainStyledAttributes
se penchera directement sur le sujet lui-même. Autrement dit, si vous ajoutez un attribut de style à votre thème, par exemple
android:textColor
, le système de visualisation le sélectionnera en dernier recours. En règle générale, c'est une mauvaise idée de mélanger les attributs de thème et les attributs de style, c'est-à-dire que ce que vous appliquez directement à la vue, en règle générale, ne doit jamais être défini pour le thème (et vice versa), mais il existe quelques rares exceptions.
Un exemple serait lorsque vous essayez de changer la police dans l'application. Vous pouvez utiliser l'une des méthodes décrites ci-dessus, mais ajuster manuellement les styles / l'apparence du texte partout sera monotone et dangereux, et les styles par défaut ne fonctionneront qu'au niveau du widget; les sous-classes peuvent remplacer ce comportement, par exemple les boutons définissent leur propre
android:buttonStyle
, que votre
android:textViewStyle
ne ramassera pas. Au lieu de cela, vous pouvez spécifier la police dans votre thème:
<style name="Theme.MyApp" parent="@style/Theme.MaterialComponents.Light"> ... <item name="android:fontFamily">@font/space_mono</item> </style>
Maintenant, toute vue qui prend en charge cet attribut le récupérera s'il n'est pas remplacé par quelque chose avec une priorité plus élevée:
Affichage → Style → Style par défaut → Thème → Apparence du texteEncore une fois, puisque cela fait partie du système de style de vue, il remplacera tout ce qui est fourni sous forme de texte, mais sera remplacé par des attributs plus spécifiques.
Rappelez-vous cette priorité. Dans notre exemple avec une police pour l'ensemble de l'application, vous pouvez vous attendre à ce que la
Toolbar
TextView
cette police, car elle contient le titre, qui est un
TextView
. La classe Toolbar elle-même, cependant, définit un style par défaut contenant
titleTextAppearance
, qui définit
android:fontFamily
et le définit directement dans l'en-tête
TextView
,
TextView
la valeur de niveau de thème. Les styles au niveau du sujet peuvent être utiles, mais faciles à remplacer, alors assurez-vous qu'ils sont appliqués correctement.
Bonus: problèmes non résolus
Cet article a été entièrement consacré à la conception déclarative du texte au niveau de la vue, c'est-à-dire comment
TextView
l'intégralité de
TextView
pendant le remplissage. Tout style appliqué après le remplissage (par exemple,
textView.setTextColor(…)
) remplacera le style déclaratif.
TextView
prend également en charge les styles plus petits via
Span
. Je n'entrerai pas dans ce sujet, comme il est décrit en détail dans les articles de
Florina Muntenescu .
→
Style de texte spantastique avec Spans→
Portées insuffisantesJe mentionnerai cela pour être complet, pour garder à l'esprit que le style et les étendues du programme seront en haut de l'ordre de priorité:
Span → Setters → View → Style → Style par défaut → Theme → TextAppearanceChoisissez votre style
Bien qu'il existe plusieurs façons de styliser votre texte, comprendre les différences entre les méthodes et leurs limites vous aide à trouver le bon outil pour une tâche spécifique ou à comprendre pourquoi une méthode prime sur une autre.
Ayez un joli style de texte!
Nous invitons tout le monde à un
webinaire gratuit dans le cadre duquel nous nous familiariserons avec le framework DI Dagger 2: nous étudierons comment Dagger2 génère du code, nous traiterons des annotations JSR 330 et des constructions Dagger2, nous apprendrons à utiliser Dagger2 dans une application multi-modules et considérerons l'injecteur Dagger Android.