¿Cómo se ve tu texto?

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 Poltrack

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

  1. Establezca cualquier estilo de aplicación en textViewStyle como el estilo predeterminado para su tema.
  2. 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
  3. Cree un style estableciendo atributos no admitidos por TextAppearance (que ellos mismos determinarán uno de sus TextAppearance ).
  4. 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 → Estilo

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

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 texto

Como 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:

  1. Cualquier valor de atributo en este AttributeSet.
  2. El recurso de estilo especificado en AttributeSet (denominado "estilo").
  3. El estilo predeterminado especificado por defStyleAttr y defstyleres
  4. Valores básicos en este hilo.

Orden de prioridad de estilo de la documentación del tema

Volveremos 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ándar

Por 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 texto

Como 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 texto

Nuevamente, 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 entendidos

Mencionaré 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 texto

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

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


All Articles