Amigos, genial todos los viernes. Queremos compartir con ustedes una traducción de un artículo preparado especialmente para estudiantes del curso
"Desarrollador de Android". Curso avanzado " . Que tengas una buena lectura.

Cómo dar estilo declarativamente al texto en Android.
Ilustración de Virginia PoltrackTextView
en aplicaciones de Android proporciona varios atributos para diseñar texto y varias formas de aplicarlos. Estos atributos se pueden establecer directamente en el diseño, aplicar un estilo a la vista o el tema al diseño o, si lo desea, establecer la apariencia del texto. ¿Pero qué de esto debería usarse? ¿Y qué pasa si los combinas?
¿Qué y cuándo usar?Este artículo describe varios enfoques para la estilización declarativa de texto (es decir, cuando define estilos en un archivo XML), analiza el alcance y la prioridad de cada método.
tl; dr;
Deberías leer la publicación completa, pero aquí hay un resumen.
Recuerde el orden de prioridad de varios métodos de estilo: si está tratando de estilizar un texto y no ve los resultados esperados, lo más probable es que sus cambios sean anulados por algo con una prioridad más alta en esta jerarquía:
Jerarquía de los métodos de diseño de texto.Sugeriría el siguiente procedimiento para diseñar:
- Establezca cualquier estilo de aplicación en
textViewStyle
como el estilo predeterminado para su tema. - Instale el conjunto (pequeño) de
TextAppearance
que TextAppearance
su aplicación (o usará / heredará de los estilos de MaterialComponent) y haga referencia a ellos directamente desde su vista - Cree un
style
estableciendo atributos no admitidos por TextAppearance
(que ellos mismos determinarán uno de sus TextAppearance
). - Realice cualquier estilo único directamente en el diseño.
Mostrar algo de estilo
Puede establecer directamente los atributos de
TextView
en el diseño, pero este enfoque puede ser más tedioso e inseguro. Imagine que de esta manera está intentando actualizar el color de todos los TextViews en la aplicación. Al igual que con todas las demás vistas, puede (¡y debería!) Usar estilos en su lugar para garantizar la coherencia, la reutilización y la facilidad de actualización. Para este propósito, recomiendo crear estilos para el texto siempre que probablemente desee aplicar el mismo estilo a múltiples vistas. Esto es extremadamente simple y es ampliamente compatible con el sistema de visualización de Android.
¿Qué sucede debajo del capó cuando le das estilo a la vista? Si alguna vez escribió su vista personalizada, probablemente vio la llamada a
context.obtainStyledAttributes (AttributeSet, int [], int, int) . Por lo tanto, el sistema de visualización en Android pasa a la vista los atributos especificados en el diseño. El parámetro
AttributeSet
, de hecho, puede considerarse como un mapa de los parámetros XML que especifique en su diseño. Si AttributeSet establece el estilo,
primero se lee el
estilo y luego se le aplican los atributos especificados directamente en la vista. Por lo tanto, llegamos a la primera regla de prioridad.
Ver → EstiloLos atributos definidos directamente en la vista siempre "prevalecen" y anulan los atributos definidos en el estilo. Tenga en cuenta que se aplica una
combinación de estilo y atributos de vista; definir un atributo en la vista, que también se especifica en el estilo,
no cancela todo el estilo. También debe tenerse en cuenta que, en su opinión, no hay una forma real de determinar de dónde viene la estilización; El sistema de visualización lo decide una vez en una llamada similar. No puede obtener ambas opciones y elegir.
Aunque los estilos son extremadamente útiles, tienen sus limitaciones. Una de ellas es que solo puede aplicar un estilo a la vista (a diferencia de algo como CSS, donde puede aplicar varias clases).
TextView
, sin embargo, tiene un truco; proporciona el atributo
TextAppearance
, que funciona de manera similar al
style
. Si
TextAppearance
style
texto mediante
TextAppearance
, deje el atributo de
style
libre para otros estilos, lo que parece práctico. Echemos un vistazo más de cerca a qué
TextAppearance
y cómo funciona.
Aparición de texto
TextAppearance
no tiene nada de mágico (por ejemplo, un modo secreto para aplicar varios estilos que no debería conocer),
TextView
ahorra trabajo innecesario. Echemos un vistazo al constructor
TextView
para comprender lo que está sucediendo.
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(); }
Entonces, ¿qué está pasando aquí? Esencialmente,
TextView
primero busca ver si especificó
android:textAppearance
, si es así, carga este estilo y aplica todas las propiedades que se enumeran allí. Más tarde, carga todos los atributos de la vista (que recuerda, incluido el estilo) y los aplica. Entonces llegamos a la segunda regla de prioridad:
Ver → Estilo → Apariencia de textoComo la apariencia del texto se verifica primero, cualquier atributo definido directamente en la vista o en el estilo lo anulará.
Con
TextAppearance
, hay otra
TextAppearance
a tener en cuenta: admite un
subconjunto de los atributos de estilo que ofrece
TextView
. Para comprender mejor lo que quiero decir, volvamos a esta línea:
obtainStyledAttributes(ap, android.R.styleable.TextAppearance);
Examinamos la versión
receiveStyledAttributes
con 4 argumentos, esta versión de 2 argumentos es ligeramente diferente de ella. Ella mira el estilo dado (como lo define la primera
id
parámetro) y lo filtra solo de acuerdo con los atributos en el estilo que aparecen en el segundo parámetro, la matriz de
attrs
. Así que
android.R.styleable.TextAppearance
define el alcance de
TextAppearance
. Al observar esta definición, vemos que
TextAppearance
admite muchos,
pero no todos, los atributos que
admite 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" />
Atributos de estilo admitidos por TextAppearance
Estos son algunos atributos de
TextView
que no están incluidos en
TextAppearance
:
lineHeight[Multiplier|Extra]
,
lines
,
breakStrategy
y
breakStrategy
.
TextAppearance
funciona a nivel de caracteres, no a nivel de párrafo, por lo que los atributos que afectan a todo el diseño no son compatibles.
Por lo tanto,
TextAppearance
muy útil, nos permite definir un estilo orientado a los atributos de estilización del texto y deja el
style
a la vista libre para otros fines. Sin embargo, tiene un alcance limitado y se encuentra en la parte inferior de la cadena de prioridad, así que no se olvide de las limitaciones.
Valores predeterminados razonables
Cuando observamos cómo la vista de Android resuelve los atributos (
context.obtainStyledAttributes
), en realidad lo simplificamos un poco. Ella llama a
theme.obtainStyledAttributes (usando el
Theme Context
actual 'a). Al verificar el
enlace , se muestra el orden de prioridad que examinamos anteriormente, y se indican 2 lugares más que está buscando para resolver atributos: el estilo predeterminado para la vista y el tema.
Al determinar el valor final de un atributo particular, entran en juego cuatro parámetros de entrada:
- Cualquier valor de atributo en este AttributeSet.
- El recurso de estilo especificado en AttributeSet (denominado "estilo").
- El estilo predeterminado especificado por defStyleAttr y defstyleres
- Valores básicos en este hilo.
Orden de prioridad de estilo de la documentación del temaVolveremos a los temas, pero primero echemos un vistazo a los estilos predeterminados. ¿Cuál es este estilo predeterminado? Para responder a esta pregunta, creo que sería útil salir un poco del tema
TextView
y echar un vistazo a un
Button
simple. Cuando inserta
<
Button
>
en su diseño, se ve más o menos así.
Botón estándarPor qué Si observa el código fuente de
Button
, verá que es bastante escaso:
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); } }
Eso es todo! Aquí está toda la clase (sin comentarios). Puede verificarlo usted mismo
aquí . Esperaré Entonces, ¿de dónde viene el fondo, las letras mayúsculas, la ondulación, etc.? Es posible que haya fallado, pero todo se definirá en el constructor con 2 argumentos; uno que se llama cuando el diseño se toma de XML. Este es el último parámetro que define
defaultStyleAttr
en
com.android.internal.R.attr.buttonStyle
. Este es el estilo predeterminado, que es esencialmente un punto de referencia indirecto, lo que le permite especificar el estilo predeterminado. No apunta directamente al estilo, pero le permite señalar uno de los especificados en su tema que comprobará al resolver atributos. Y eso es exactamente de lo que todos los temas que generalmente heredas hacen para proporcionar la apariencia de los widgets estándar. Por ejemplo, si observa el tema Material, define
@style/Widget.Material.Light.Button
, y es este estilo el que proporciona todos los atributos que pasará
theme.obtainStyledAttributes
si no especificó nada más.
Volver a
TextView
, también ofrece un estilo predeterminado:
textViewStyle
. Esto puede ser muy conveniente si desea aplicar algunos estilos a cada TextView en su aplicación. Suponga que desea establecer el espaciado de línea predeterminado en 1.2. Puede hacer esto con
style/TextAppearance
e intentar aplicarlo durante una revisión de código (o tal vez incluso con una elegante regla personalizada en Lint), pero debe estar atento y asegurarse de reclutar nuevos miembros del equipo , tenga cuidado con la refactorización, etc.
Un mejor enfoque sería especificar su propio estilo predeterminado para todas las
TextView
de
TextView
en la aplicación, estableciendo el comportamiento deseado. Puede hacerlo configurando su propio estilo para
textViewStyle
, que se hereda de la plataforma o de
MaterialComponents/AppCompat
de forma predeterminada.
<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>
Con esto en mente, nuestra regla de prioridad toma la forma:
Ver -> Estilo -> Estilo predeterminado -> Apariencia de textoComo parte de la resolución de los atributos del sistema de visualización, este espacio se completa después de los estilos (de modo que todo en el estilo se cancela por el estilo aplicado o los atributos de visualización por defecto), pero aún anulará la apariencia del texto. Los estilos predeterminados pueden ser muy convenientes. Si alguna vez decide escribir su propia vista personalizada, pueden ser una forma poderosa de implementar el comportamiento predeterminado, facilitando la personalización.
Si hereda el widget y no especifica su propio estilo predeterminado, asegúrese de usar el estilo de clase principal predeterminado en los constructores (no pase solo 0). Por ejemplo, si hereda de
AppCompatTextView
y escribe su propio constructor con 2 argumentos, asegúrese de pasar
android.R.attr.textViewStyle defaultStyleAttr
(
como aquí ), de lo contrario perderá el comportamiento de la clase padre.
Temas
Como se mencionó anteriormente, hay otra forma (última, lo prometo) de proporcionar información de estilo. Otro lugar
theme.obtainStyledAttributes
se verá directamente en el tema en sí. Es decir, si agrega un atributo de estilo a su tema, por ejemplo
android:textColor
, el sistema de visualización lo seleccionará como último recurso. Como regla general, es una mala idea mezclar atributos de tema y atributos de estilo, es decir, lo que aplica directamente a la vista, como regla, nunca debe establecerse para el tema (y viceversa), pero hay un par de raras excepciones.
Un ejemplo sería cuando intente cambiar la fuente en toda la aplicación. Puede usar uno de los métodos descritos anteriormente, pero ajustar manualmente los estilos / apariencia del texto en todas partes será monótono e inseguro, y los estilos predeterminados solo funcionarán en el nivel del widget; las subclases pueden anular este comportamiento, por ejemplo, los botones definen su propio
android:buttonStyle
, que su
android:textViewStyle
no recogerá. En cambio, puede especificar la fuente en su tema:
<style name="Theme.MyApp" parent="@style/Theme.MaterialComponents.Light"> ... <item name="android:fontFamily">@font/space_mono</item> </style>
Ahora, cualquier vista que admita este atributo lo recogerá si no es anulado por algo con una prioridad más alta:
Ver → Estilo → Estilo predeterminado → Tema → Apariencia de textoNuevamente, dado que esto es parte del sistema de diseño de vistas, anulará todo lo que se proporciona en forma de texto, pero será anulado por cualquier atributo más específico.
Recuerda esta prioridad. En nuestro ejemplo con una fuente para toda la aplicación, puede esperar que la
Toolbar
seleccione esta fuente, ya que contiene el título, que es un
TextView
. Sin embargo, la clase Toolbar en sí misma define un estilo predeterminado que contiene
titleTextAppearance
, que define
android:fontFamily
, y lo establece directamente en el encabezado
TextView
, anulando el valor del nivel del tema. Los estilos de nivel de tema pueden ser útiles, pero fáciles de anular, así que asegúrese de aplicarlos correctamente.
Bonus: problemas no resueltos
Todo este artículo se dedicó al diseño declarativo de texto en el nivel de vista, es decir, cómo
TextView
todo el
TextView
durante el llenado. Cualquier estilo aplicado después del relleno (por ejemplo,
textView.setTextColor(…)
) anulará el estilo declarativo.
TextView
también admite estilos más pequeños a través de
Span
. No voy a entrar en este tema, como se describe en detalle en los artículos de
Florina Muntenescu .
→
Estilo de texto fantástico con tramos→
Tramos entendidosMencionaré esto para completar, para tener en cuenta que el estilo y la amplitud del programa estarán en la parte superior del orden de prioridad:
Span → Setters → Ver → Estilo → Estilo predeterminado → Tema → Apariencia de textoElige tu estilo
Aunque hay varias formas de diseñar su texto, comprender las diferencias entre los métodos y sus limitaciones lo ayuda a encontrar la herramienta adecuada para una tarea específica o comprender por qué un método tiene prioridad sobre otro.
¡Que tengas un buen estilo de texto!
Invitamos a todos a un
seminario web gratuito en el marco del cual nos familiarizaremos con el marco DI Dagger 2: estudiaremos cómo Dagger2 genera código, trataremos las anotaciones JSR 330 y las construcciones Dagger2, aprenderemos cómo usar Dagger2 en una aplicación de módulos múltiples y consideraremos el inyector Dagger para Android.