À quoi ressemble votre texte?

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 Poltrack

TextView 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 texte

Je suggère la procédure suivante pour le style:

  1. Définissez n'importe quel style d'application dans textViewStyle comme style par défaut pour votre thème.
  2. 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
  3. 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 ).
  4. 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 → Style

Les 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(); } // a little later a = theme.obtainStyledAttributes(attrs, com.android.internal.R.styleable.TextView, defStyleAttr, defStyleRes); readTextAppearance(context, a, attributes, true); 

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:

  1. Toutes les valeurs d'attribut dans cet AttributeSet.
  2. La ressource de style spécifiée dans AttributeSet (nommée "style").
  3. Le style par défaut spécifié par defStyleAttr et defstyleres
  4. Valeurs de base dans ce fil.

Ordre de priorité de style dans la documentation du thème

Nous 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 standard

Pourquoi? 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 texte

Dans 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 texte

Encore 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 insuffisantes

Je 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 → TextAppearance

Choisissez 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.

Source: https://habr.com/ru/post/fr456110/


All Articles