Best practices for text on Android (Google I/O ’18)

[REPRODUCIENDO MÚSICA] CLARA BAYARRI: ¡Buenas tardes! Bienvenido a Mejores prácticas
para texto en Android. Mi nombre es Clara Bayarri
y soy líder técnica en el equipo de Android Tool Kit. FLORINA MUNTENESCU:
Soy Florina Muntenescu y soy una
defensora de los desarrolladores de Android. SIYAMED SINIR: Siyamed Sinir,
líder tecnológico de Android Text. CLARA BAYARRI: Entonces ¿por qué
damos una charla sobre texto? Bueno, resulta que el texto es
una parte importante de tu aplicación. Es parte de tu diseño.

Es parte de tu marca. Es parte de quién eres. Y es lo más importante que
consumirán sus usuarios. Es el front-front, lo primero
que leen los usuarios y lo que
afectará principalmente la legibilidad, la accesibilidad, la
usabilidad, todo eso. Además de eso,
Android Text Stack es responsable de los
emoji, que a todos nos encantan. Pero todo esto obviamente
tiene un costo. Así que hay un montón de
implicaciones de rendimiento relacionadas con el texto que
queremos que usted tenga en cuenta. Así que hoy
repasaremos algunas de las mejores prácticas y
cosas que debes saber. Antes de que podamos hacer
eso, hablemos un poco sobre la arquitectura. Queremos explicar cómo
se integra la pila tecnológica en Android, para que puedas
entender mejor de qué hablaremos más adelante.

La pila de texto en Android
se divide en dos partes. Está el código Java y luego
está el código nativo o C++. En la parte superior
de la capa de Java, encontrará lo
que probablemente le resulte más familiar, que
es la visualización y edición de texto. Estos son widgets que
brindan funcionalidad de texto lista para usar y hacen
todo por usted. Si tiene vistas personalizadas y
no desea usar nuestro
widget, entonces probablemente esté usando la
segunda capa, que es pintura de diseño en Canvas. Esta clase en Java
le ayudará a diseñar texto y renderizarlo sin necesidad de
utilizar nuestro widget.

Una vez que llegamos a la
parte nativa, la primera capa es una biblioteca que llamamos Minikin. Minikin nos ayuda a medir palabras
, dividir líneas y dividir palabras. Y es nuestro primer punto
de contacto con C++. Debajo de eso, hay un montón de
bibliotecas que utilizamos para ayudarnos, ICU, que se ocupa de
Unicode, HarfBuzz, que configura la tecnología
por nosotros, FreeType que genera mapas de bits
para todos los Gif y, finalmente, Skia, que
es nuestro motor gráfico. Hoy, nos
centraremos en las tres principales, ya que estas son las capas
que posee el equipo de Android y sobre las que podemos brindar la mayoría de las
mejores prácticas y sugerencias. SIYAMED SINIR: Minikin se encuentra en
el núcleo de Android Text Stack y está implementado en C++. Sus principales responsabilidades son el
diseño, la medición y el salto de línea del texto. Es importante comprender
qué partes se implementan en Minikin versus
Java, ya que eso define qué características podemos respaldar
usando la biblioteca de soporte, que no permite código nativo.

Comencemos con la
medición del texto. Si proporciona una cadena
como esta a Minikin, primero prueba para
identificar los glifos. El glifo es una representación
de un personaje. Y puedes pensar en ello
como la imagen que se va a dibujar. Sin embargo, el
[INAUDIBLE] correcto de la coincidencia de glifos no siempre es uno a uno. Aquí tenemos tres letras,
FFI que coincide con un solo glifo. Los glifos también se pueden encontrar
en diferentes fuentes, como es el caso del
carácter japonés del ejemplo. Esto se llama
cadena de reserva de fuentes. Una vez identificados todos los glifos
, el sistema puede
posicionarlos juntos para tener el aspecto final. Sin embargo, en este punto, el
posicionamiento no siempre consiste en colocar los glifos
uno al lado del otro, como es el caso de la
letra e y el acento agudo.

Si proporciona una cadena más larga
, Minikin primero la divide en palabras. Y para cada palabra,
hace la medición. El resultado de esta medición
se agrega a un caché para que, si el sistema vuelve a
encontrar la misma palabra , pueda aumentar
el valor calculado. Se trata de una
caché LRU para todo el sistema y tiene un tamaño fijo de 5000 elementos. Entonces la cadena es más ancha que
la prueba de matriz [INAUDIBLE] Minikin tiene que hacer un salto de línea. En el caso más simple, se trata de
colocar las cajas una al lado de la otra hasta alcanzar el límite
y luego pasar a la siguiente línea. El comportamiento se controla con
el atributo de estrategia de ruptura. Y si cambiara
de simple a equilibrado aquí, Minikin distribuirá
[INAUDIBLE] entre las líneas para tener un mejor elemento de texto. El valor predeterminado
es alta calidad, que es muy
similar a equilibrado, excepto por algunas diferencias sutiles,
una de ellas es la separación de palabras en la última línea.

La separación de palabras mejora la
alineación del texto y el uso de espacios en blanco, y
se vuelve cada vez más importante a medida que el ancho de la línea se acorta. Aunque esto
tiene un costo. El costo surge
porque ahora el sistema tiene que medir más
palabras y tiene que comparar más configuraciones
para encontrar una solución óptima. Después de Android P,
habilitar la separación de palabras es 2 veces y media
más caro que el estado deshabilitado. En Android P mejoramos el
caso de estrategia de interrupción simple. Sin embargo, la configuración predeterminada
sigue teniendo el mismo coste. Otra cosa que afecta las
separaciones silábicas es la ubicación.

En este ejemplo, tenemos un
dispositivo en la configuración regional inglesa y dos cadenas de ejemplo. Uno de ellos no tiene
guiones correctamente y el otro, el
último, usa el glifo correcto para el texto japonés. Esto sucederá principalmente
con texto cargado dinámicamente, como estas cosas
cargadas desde la red. Y sucede porque
el sistema no conoce los idiomas
de esas cadenas. Para solucionar este problema, tenemos que informarle al
sistema sobre el idioma usando la función setTextLocale. Si la cadena contiene
varios idiomas, puede usar LocaleSpan para marcar
diferentes partes de la cadena de palabras. Sin embargo, tenga en cuenta que
la cantidad de espacios regionales afecta el rendimiento
del diseño del texto. FLORINA MUNTENESCU: Una
de las tareas más comunes cuando se trabaja con
texto es el estilo del texto. Por lo general, comenzamos a
trabajar con los atributos del texto y configuramos el texto
especial, el color del texto y la fuente. Por eso lo llamamos estilo único
porque todos estos atributos afectan a todo el texto
desde el campo de texto.

Pero normalmente para el
diseñador esto no es suficiente. Y querrán
diseñar específicamente una palabra o un párrafo. Entonces lo que quieren es aplicar un
estilo múltiple al mismo bloque de texto. Entonces, para lograr esto
en Android trabajamos con intervalos. Abarca nuestros objetos de marcado
que se pueden adjuntar al texto desde un inicio, tono e índice. Y los intervalos se pueden clasificar
en intervalos de caracteres y de párrafos , dependiendo de si se
aplican sólo a unos pocos caracteres o a párrafos completos. Y luego la extensión de los caracteres se
puede dividir en apariencia y efecto métrico. Y la diferencia
entre ellos radica en el método que se debe
llamar en la vista de texto para poder representar la diapositiva. Por lo tanto, los tramos que afectan la apariencia
requieren que se vuelva a dibujar, mientras que los
tramos que afectan la métrica requieren tanto una nueva medición como un nuevo dibujo.

Veamos un ejemplo. Entonces digamos que necesitamos
diseñar un bloque de código. Entonces, para hacer
esto, primero cambiaríamos el color de fondo. Entonces, para esto, usaríamos
una gama de colores de fondo. Esto no cambia
la métrica del texto. Entonces esto significa que esta es una
apariencia que afecta la duración. Pero también necesitamos
cambiar la fuente. Entonces, para esto
usaríamos span tipo de letra. Pero debido a que las fuentes también
cambian el tamaño del texto, esta es una métrica que
afecta la duración. Para aplicar estilo a bloques de
texto, por ejemplo, cambiando el
margen de diseño o dibujando una viñeta delante del
texto, se utilizaría una extensión de viñeta. Por lo tanto, los intervalos de párrafos,
al igual que los de viñetas, deben adjuntarse desde el
principio del párrafo y afectar a todo el
texto del párrafo. La cuestión es que si
no lo adjuntas desde el principio del
párrafo, lo más probable es que pierdas el
estilo por completo.

Entonces, en el marco
definimos una serie de interfaces y
clases abstractas. Y bajo el
capó, el marco busca instancias
de estas clases y luego activa diferentes
acciones dependiendo de ellas. Pero en realidad lo que
hace que los intervalos sean tan poderosos es el hecho de que te dan
acceso a cosas como las pinturas de texto o el lienzo, en el
caso de los intervalos de algunos párrafos. Esto significa que puedes
manejar prácticamente todo lo que necesitas en términos de texto. Entonces, en primer lugar,
siempre que necesite un intervalo, verifique los que están
disponibles en el marco. Porque cubren la mayoría
de los casos de uso comunes. Pero si volvemos
a nuestro ejemplo, vemos que el tipo de letra
solo obtiene un tipo de letra que comienza con Android P.
Esto significa que si queremos admitir
dispositivos P gratuitos, queremos crear nuestro propio tipo de letra personalizado.

Entonces, para hacer esto, expandiríamos
desde la métrica que afecta el intervalo. Y esta clase nos da
acceso a dos métodos. Para actualizar el estado de la medida
, que se llama cuando se
llama a textView onMeasure, y actualizar el estado de dibujo, que se
llama cuando se llama al método textView onDraw . Y ambos métodos
nos dan acceso a textPaint. Entonces, en TextPaint
todo lo que tenemos que hacer es cambiar el tipo de letra. Pero si tenemos textPaint,
significa que también podemos configurar el color de fondo. Entonces significa que podemos
simplemente crear un grupo de bloques de código para diseñar nuestro bloque de código. Ahora tenemos dos
formas diferentes de diseñar nuestro bloque de código, una con la composición de
múltiples tramos, otra desde el marco
y otra personalizada, y otra forma en la que
usamos un tramo personalizado. Entonces, ¿cuál deberías usar? Para ayudarle a decidir, hay un
dato importante que debe recordar. Sólo se
pueden parcelar los tramos de la estructura. Entonces, hay dos casos comunes
cuando los intervalos están parcelados: primero, cuando
pasamos texto a través de intenso o cuando copiamos texto.

Independientemente, para copiar
texto, por ejemplo, desde una vista de texto a una edición de texto
en la misma actividad, o si estamos copiando
texto de una aplicación a otra, ese texto se puede
dividir y descomprimir a través del servicio del portapapeles. Entonces esto es lo que sucederá. Si pasamos el texto con
un intervalo de marco y un intervalo personalizado, bueno, solo nuestro
marco abarca lo que pasamos. Entonces esto significa que estamos perdiendo
nuestra variedad de tipos de letra personalizados. Pero entonces, si pasamos un
texto con un intervalo personalizado, eso significa que no se
mantendrá ningún estilo.

Entonces, en general, cuando
implementa intervalos y el texto se divide,
considere el comportamiento que desea. ¿ Debería mantenerse parcialmente el estilo
o no? CLARA BAYARRI: Ahora
que sabes acerca de los intervalos, ¿ cómo los usamos en el texto? Bueno, el marco proporciona
dos interfaces clave para esto. El primero está extendido. Spanned es para
etiquetas inmutables y marcado inmutable, y declara métodos
como get spans o get span start
y get span end. Permite consultar intervalos,
pero no modificarlos. Si desea
modificarlos, tenemos Spannable, que tiene
la capacidad de mutar el marcado con métodos que
establecen la extensión o eliminan el spam. Luego tenemos tres
implementaciones concretas de estas interfaces. El primero es SpannedString,
que implementa Spanned y, como tal, tiene
texto inmutable y marcado inmutable. SpannableString, que
implementa Spannable, como tal tiene
texto inmutable pero marcado mutable.

Y finalmente,
tenemos una tercera clase, que agrega mutabilidad
al texto, que es SpannableStringBuilder. Esto es lo que usamos
internamente, por ejemplo, para editar texto, porque
te permite modificar el texto y el marcado. Estas dos últimas clases son
muy diferentes internamente. SpannableString contiene
una serie de tramos, mientras que SpannableStringBuilder
contiene un árbol de tramos. Por lo tanto, estos se
implementan de manera muy diferente. Entonces, como equipo, nos preguntábamos: ¿
es un árbol más eficiente? Tal vez haya un caso en el que
queramos recomendar a las personas que utilicen únicamente
SpannableStringBuilder todo el tiempo porque
es más eficiente.

Entonces hicimos pruebas. La respuesta es hasta 250
vanos, son iguales. Básicamente se desempeñan por igual. Por lo tanto, le recomendamos que haga lo que
tenga más sentido semántico, que es utilizar SpannableString
para texto inmutable y SpannableStringBuilder
para texto mutable. Sin embargo, después de 250 tramos,
realmente empiezan a divergir. Y le recomendamos que utilice
SpannableStringBuilder para todos sus casos simplemente
porque ofrece más rendimiento.

Por curiosidad, llevamos
estas pruebas al límite. Resulta que cuando
tienes miles de tramos, realmente divergen. Pero realmente
esperamos que este no sea un caso de uso común para nadie. Otra cosa común que
vemos con los intervalos es que las personas intentan
verificar si un intervalo está presente dentro de un intervalo que se puede abarcar. Y la solución que hemos visto
en línea usa span.getspans. Entonces es algo
como esto, simplemente consulta todos los
intervalos de un tipo determinado, obtiene una matriz y luego simplemente
verifica si la matriz está vacía. Esto es extremadamente ineficiente. Estás recorriendo
todo el intervalo buscando todas las
instancias de ese intervalo, reuniéndolas
todas en una matriz y luego simplemente verificando
si esa matriz está vacía. Hay una mejor manera de hacer esto,
que es nextSpanTransition. Este método comprueba
los límites de los tramos. Entonces, cuando solicite el primer
límite de un determinado tramo, simplemente tendrá que hacer el trabajo
hasta encontrar el primer tramo, y luego podrá detenerse. Y no recopilará ningún tramo
en una matriz, desperdiciando memoria que no debería hacer.

FLORINA MUNTENESCU:
Una tarea frecuente cuando se trabaja con
estilos de texto es diseñar texto internacionalizado. Por lo general, en sus
recursos definirá la misma cadena
en varios idiomas. Pero luego necesitas
resaltar una palabra específica. Aquí, por ejemplo, queremos que
la palabra texto esté en negrita. Pero es que la
palabra y el lugar donde aparece es diferente en cada idioma. Entonces, una solución sencilla
sería utilizar simplemente texto HTML. Pero el problema con
esto es que el texto HTML tiene una funcionalidad limitada. Entonces, si, por ejemplo,
queremos cambiar la fuente, esto no es algo
que HTML pueda proporcionar. La solución para esto es
utilizar etiquetas de anotación. Entonces, la etiqueta de anotación
nos permite establecer pares de clave y valor y luego definir
lo que necesitemos.

Así que aquí, por
ejemplo, estoy definiendo la fuente clave y el
valor como el nombre de la fuente. Y así es como
lo usaríamos en el código. Entonces obtendríamos el
texto de los recursos como una cadena distribuida. Luego obtendríamos las
anotaciones del tramo. [INAUDIBLE] los revisaríamos
y obtendríamos los que tienen la
clave que nos interesa y luego el valor. Y luego establecería el
intervalo en función de los índices. SIYAMED SINIR:
Ahora veremos cómo se presenta el texto en la vista.

Supongamos que
tiene un campo de texto con algo de relleno aplicado. Cuando configura el robot, el
texto reposicionará la imagen en la parte superior de la vista
después del relleno.
La función Obtener relleno superior compuesto obtendrá solo la distancia
desde la parte superior de la vista hasta la parte inferior de la imagen. Configurar el relleno dibujable
con ese espacio adicional entre la imagen y la idea
de que se dibujará el texto. Y ahora obtener el
relleno compuesto superior apuntará a esta nueva ubicación. Luego configuras el texto. Texto crearemos
un objeto de diseño. El diseño es responsable de
representar y medir el texto. Aquí, los límites
del diseño están resaltados en naranja. También le proporcionará
más información sobre el texto que contiene,
como la línea base superior e inferior de las líneas. Hay tres
clases en capas en Android. BoringLayout es para un
estilo único y texto simple, que no contiene caracteres
superiores, de línea siguiente ni de derecha o izquierda. DynamicLayout se crea para
texto editable y seleccionable.

Y para otros casos, textView
creará un diseño estático. Cuando configuramos la
gravedad, el objeto de diseño en sí se
ubicará en la vista. Obtener la parte superior del relleno total
lo devolverá a una distancia desde la parte superior de la
vista hasta la parte superior del diseño. IncluirFontPadding. Este atributo
agregará espacio adicional en la parte superior e inferior del diseño. Y la altura del
espaciado se calcula utilizando los valores de la fuente. El efecto es más visible
que si utilizas una escritura alta. En este ejemplo, cuando
se establece en falso, vemos que el texto se recorta. Dado que solo se aplica
cuando se establece en verdadero, solo se aplica al espaciado entre la parte
superior e inferior del texto, en Android veremos
que las líneas se superpondrán.

En Android P
solucionamos este problema. Ahora, si el sistema identifica
que las líneas se superpondrán, pondrá un espacio adicional
entre las líneas y aplicará esta regla
a todo el párrafo. Para proporcionar una opción de exclusión voluntaria
, agregamos un atributo llamado
fallbackLineSpacing, que está activado de forma predeterminada. Un atributo similar que
cambia la altura de la línea es el elegante
atributo de altura del texto. Sin embargo, es poco probable que, aunque
cambie la altura de la línea, a diferencia de los otros atributos,
no agregará espacios, sino que comenzará a
usar una fuente diferente.

Hay dos fuentes diferentes
definidas en la cadena alternativa de fuentes de Android,
compactas y elegantes. Configurar la altura del
texto elegante para hacerlo hará que el sistema
elija la versión elegante del mismo script
si esa fuente existe. Hablando de altura de línea,
es un atributo importante para la legibilidad
del texto, y no puedes controlarlo usando
interlineado adicional y multiplicando su atributo. Estos atributos
agregarán espacio adicional entre las líneas,
pero no en la última línea. Como los desarrolladores
nos mencionaron, los diseñadores en su mayoría
ofrecen la noche de línea tal como está, pero no como
multijugador o extra.

En Android P, agregamos
el atributo de altura de línea. Otros dos atributos que
agregamos en Android P para cerrar
la brecha entre el diseño y la implementación son
FirstBaselineToTopHeight y LastBaselinetoBottomHeight. El primero
te permitirá controlar la distancia entre
la parte superior de la vista y la línea de base
de la primera línea. Y el segundo de
manera similar, te permitirá controlar la distancia
entre la parte inferior de la vista y la línea de base
de la última línea. Finalmente, cuando
configura la alineación del texto, el texto se
colocará en el diseño.

Las funciones Obtener línea a la izquierda y Obtener línea a la derecha
del objeto de diseño le brindarán información
sobre este posicionamiento en relación con los
límites del diseño. Ahora que conoce el diseño
y algunas funciones importantes, podemos responder una
pregunta frecuente en Internet: ¿ cómo se puede implementar
un fondo redondeado? Esto no es posible con Spans,
pero podemos dibujarlo nosotros mismos usando las funciones de diseño. Para hacer eso, primero debemos
marcar las palabras para las que queremos dibujar el fondo. La anotación, que
mencionó Florina, es una buena opción
para tal caso de uso. Luego definiremos nuestros
dibujables, tres para las palabras que comienzan y terminan en
líneas diferentes, y uno para las palabras que comienzan y terminan
en la misma línea.

Revisaremos nuestras propias
anotaciones para encontrar nuestras palabras. Luego, para cada anotación, utilizando
los índices inicial y final, queremos conocer
el número de línea. Y podemos usar get line
para la función de desplazamiento del diseño, que
convertirá el índice de carácter en un número de línea. Para las palabras que comienzan
y terminan en lo mismo, también queremos aprender sobre
las coordenadas verticales de la línea. Podemos alinearnos y obtener líneas
mediante las funciones para esto. Y finalmente,
queremos aprender sobre las
coordenadas horizontales de las palabras y obtener la
función horizontal principal para convertir el índice correcto
en coordenadas horizontales relativas a los
límites del diseño. Ahora que conocemos el rectángulo
que queremos dibujar, podemos dibujar nuestro dibujable. El otro caso,
donde las palabras comienzan y terminan en una línea diferente,
tendrán casi exactamente el mismo código, excepto que ahora
tiene que identificar más rectángulos en el dibujo. CLARA BAYARRI: TextView pasa
por las mismas tres fases que cualquier vista en Android:
mide, diseña y dibuja.

Measure es, con diferencia, el más
caro para textView. Aquí es donde creamos los
diseños de los que acabamos de hablar o los recreamos si es
necesario, y donde decidimos el ancho y
el alto de la vista. Este es un trabajo realmente costoso. Más tarde, y en el diseño,
apenas hacemos nada, solo algo de desplazamiento y ajuste de tamaño automático. Y finalmente, en onDraw
emitimos los comandos de dibujo. Pero la medida es realmente
cara y hay que ser
muy consciente de ello. Es importante distinguir
qué causa un onMeasure y qué causa un onDraw. Como Florina mencionó antes,
cualquier cosa que haga que el texto cambie de tamaño
o de cómo debe distribuirse, como el
espaciado entre letras, el tipo de letra, tal vez el tamaño del texto,
tendrá que activarse en Medida, ya que necesitamos
recalcular eso. diseño, mientras que si cambias
algo que solo cambia la apariencia, pero no cómo
colocamos el texto en la pantalla, como el color del texto en el que
vas a pintarlo, eso solo necesita
activar un onDraw, que es mucho, Mucho más barato que
hacer un onMeasure también.

Veamos estas
dos fases por separado. Entonces tenemos una vista de texto como esta. He configurado un texto que
tiene varios párrafos. Y luego veo tramos para
diseñar algunas de las palabras que contiene. He cambiado el tamaño del texto. He cambiado los colores. Bueno, la primera opción
para medir su texto es un método en
Paint llamado medirTexto. Un claro indicio
de este método es que se necesita una cadena,
no un elemento que se pueda abarcar. Entonces, ¿qué hará esto? La pintura simplemente no
entiende de tramos. Entonces tomará el
texto que le has dado ( tampoco entiende los
saltos de línea), entonces colocará todo el
texto como una línea muy larga, ignorará todos los
intervalos y te dará el ancho de esa línea.

texto. La siguiente opción es utilizar
soportes, también de Paint. Una vez más, los muertos lo delatan. Se necesita una cuerda. Entonces hará exactamente lo mismo. Sin saltos de línea, sin tramos. Lo colocará todo junto. Y le dará
el cuadro delimitador de todos sus glifos,
que es un ancho y un alto. Es importante notar
que estos dos métodos devuelven valores ligeramente diferentes. Entonces textPaint.measureText
le devolverá el siguiente glifo avanzado. Aquí es donde
colocará el siguiente glifo si agrega
otro, mientras que getTextBounds le dará el
cuadro delimitador de los glifos que tiene. Y estos dos valores provienen de
diferentes partes de la fuente. Entonces pueden ser muy diferentes. Si queremos mejorar
esto, usamos diseños. Layout tiene un método estático
llamado getDesiredWidth. Lo que esto hace es que se
necesita un mensaje de texto que usted le da. Coloca cada
párrafo en una línea y, teniendo en cuenta
todo el estilo, mide cada una de las líneas. Y luego devuelve el ancho
de la línea más larga que tenga. Entonces te dará el
ancho del párrafo más largo que le hayas dado.

Esto es útil para saber
cuánto quiere ser su texto antes de poder restringirlo. Si conoce el
ancho de su vista, entonces realmente
crea un objeto de diseño. En este caso, hemos
creado un diseño estático. Al tener el
ancho restringido que desea ajustar, los diseños pueden calcular
cómo redistribuir el texto y cómo hacerlo encajar, y
darle una altura a cambio.

Entonces, al darle un ancho, podemos
calcular la altura. Además, como Siyamed
mencionó antes, obtendrá mucha
más información de su objeto de diseño. Para cada línea,
le dará cosas como la línea base, el inicio de las líneas,
el final de la línea, etc. Hemos medido el rendimiento
de todos estos métodos uno al lado del otro. El texto medido es el más barato. Entonces, en comparación con
medir texto, verá que el diseño getDesiredWidth
y Paint getTextBounds son bastante económicos de ejecutar. Son similares. Pero una vez que comienzas a
crear un diseño estático, o una vez agregas una
vista de texto para medir el texto, eso se vuelve cada vez
más costoso. Ahora que sabe qué hace cada uno
de estos métodos, asegúrese de utilizar el que tenga
sentido para su caso de uso. De manera similar, hemos hablado antes
sobre el caché de medición de palabras y cómo medimos las palabras una vez
y las colocamos en un caché. Bueno, en el caso de la pintura, cuando llamas a
medir texto en la misma palabra por segunda vez, resulta que solo
nos costó alrededor del 3% del trabajo devolver ese valor,
lo cual es una gran ganancia.

Entonces, una vez que tenemos un acierto en
el caché, es solo el 3% del trabajo. Si medimos esto en la
vista de texto, onMeasure requiere entre el 16 %
y el 11 % del trabajo que realizó la primera
vez que vio una palabra. Definitivamente usar ese caché
es realmente importante para nosotros. Como dije varias
veces, la medición es realmente costosa. ¿ Qué podemos hacer al respecto? Bueno, en P, hemos
creado una nueva característica llamada texto premedido. La idea detrás de esto es que
sabemos que en onMeasure necesitamos tomar todos los
glifos, encontrarlos en la fuente, colocarlos uno al lado del otro
y medir todas estas palabras que colocaremos en el caché. Entonces, lo que
hace el texto premedido es encargarse de esta fase por usted. Y puede ejecutar
esto en cualquier hilo, de modo que una vez que
tenga ese cálculo previo, enviarlo en la
vista de texto solo requiera alrededor del 10% del trabajo que
tuvo que hacer inicialmente, lo cual es una gran ganancia.

La forma en que esto se ve en el código es que
necesitamos un montón de atributos para saber cómo medir el texto,
cosas como la fuente que vas a usar, el tamaño del texto. Tenemos un
método conveniente en vista de texto que recuperará
todos estos datos por usted. Tiene parámetros de métricas de texto. También puede crear
esta opción usted mismo si aún no tiene la vista de texto
con la que va a trabajar. Y luego en un hilo en segundo plano
llamado precomputedtext.create.

Esto desencadena todas las cosas de las que
hemos estado hablando. Mide cada una de las palabras. Los coloca en
el caché y almacena todos esos valores. Así que esto también es
útil si vas a medir más de 5000
palabras, ya que en realidad almacena todos los valores. Luego, una vez que tenga toda
esta información precalculada, puede volver al hilo II
y configurarlo en su vista de texto. Tenga en cuenta que todo
esto sólo tiene sentido si conoce
su texto de antemano. Si simplemente conoce su texto
mientras lo va a representar, haga lo que siempre hemos
hecho, porque
de todos modos bloqueará el hilo de la interfaz de usuario.

Pero si conoce
su texto de antemano, digamos que lo está cargando
desde Internet o que tiene un
desplazamiento infinito, puede ejecutar esto, precomputar todo su
texto antes de que sea necesario mostrarlo en la pantalla,
y la ganancia es Literalmente, el 90% del trabajo se realizará
en el hilo de fondo, lo cual es asombroso. Mencione en la Biblioteca de soporte que
tenemos una solución. Para P, acabamos de ejecutar esto. Entre L y P, en realidad
no podemos hacer exactamente las competiciones que hemos hecho, pero podemos
calentar ese caché para ti. Entonces eso es lo que hacemos. Y luego, ante L, lamentablemente
no podemos hacer nada. Así que no hay problema, pero
tenemos una solución en las bibliotecas de soporte. Otra cosa común que
vemos relacionada con todo esto es el texto muy largo. La gente tiende a decir simplemente,
oh, ya tengo el mensaje de texto. Lo configuraré
todo en TextView. Cuando configura texto en textView,
mide todas las palabras y presenta todo el texto que le
ha proporcionado, incluso si nos ha proporcionado un libro completo.

Entonces, esto puede ser un
gran impacto en el rendimiento si configuras un texto muy largo
que luego no se muestra en la pantalla, porque
tal vez el usuario nunca se desplaza. Una solución a esto es dividir el
texto en partes separadas, digamos, por ejemplo,
párrafos, y colocarlos todos en un recyclerView
como elementos diferentes. Luego, de esta manera, mientras
el usuario se desplaza, comenzaremos a cargar
los siguientes bits, pero en realidad no activaremos
una medida en el diseño
de todos sus textos. También puedes vincular esto al
texto medido previamente, del que acabo de hablar,
y medir previamente todas las medidas de fondo. Entonces tendrás
toda esa información de manera realmente eficiente. Entonces hemos hablado de medida. ¿ Qué pasa con el empate? Sortear es mucho
más barato, pero también tienes varias opciones si vas a
conducir tú mismo. Uno fácil que la gente
tiende a decir, oh, ¿ por qué no simplemente
lienzo.drawtext? Eso es fácil, ¿verdad? Resulta que el lienzo
no entiende los intervalos ni los saltos de línea
ni ninguna de las cosas de las que hemos estado hablando.

De manera similar a las cosas de las que
hablamos antes, simplemente dibujará
el texto que le proporciones sin estilo como una sola ejecución. En cambio, si tiene
estilo o si desea utilizar saltos de línea,
utilice nuevamente las clases de diseño. Puedes ver el tema recurrente. Todas las clases de diseño tienen
un método de dibujo que realmente dibujará en el lienzo por usted. FLORINA MUNTENESCU: Creo que es
hora de que establezcamos el texto, pero de forma escénica. Entonces, establecer texto, creo que es el
método más utilizado. Y es excelente para
vistas de texto que no cambian, por lo que tanto el texto como
el marcado adjunto son inmutables. Entonces, bajo el capó, textView
crea una copia defensiva como una cadena extendida. Esto significa que
si cambia la referencia original,
textView no se actualiza.

Pero ¿qué pasa si queremos
mutar los tramos más adelante? O para hacer esto,
enviaríamos el texto usando el tipo de búfer expandible. Entonces, bajo el capó, textView
creará una copia del mismo, pero como expandible. Esto significa que si bien
el texto es inmutable, los intervalos son mutables. Entonces, una vez que
podemos hacer aquí es obtener el texto como un
abarcable, y luego podemos agregarle o eliminar espacios. Y sé que
tendrás la tentación de llamar al técnico o enviar mensajes de
texto nuevamente, pero ya no es necesario que hagas esto. Porque textView tiene un
detector de cambios que abarca todo el espectro. Y automáticamente sabe cuándo
se agregó o eliminó algo, y el texto
simplemente se mostrará. Pero ¿qué pasa si quieres
cambiar una propiedad interna del tramo? Entonces, en nuestro caso, queremos
cambiar ese tipo de letra en nuestro grupo de tipos de letra personalizado.

Bueno, en este caso, textView
no sabe qué ha cambiado. Y no sabe que
necesita hacer algo. Entonces tenemos que contarlo. Y haremos esto con el
diseño de la solicitud o con la invalidación, según el tipo
de atributo que haya cambiado. Entonces, si cambia la
medida que afecta el atributo y textView necesita
volver a medir y luego volver a dibujar, llamará al diseño de solicitud. Pero si simplemente cambia un
atributo que afecta la apariencia, entonces solo necesita
invalidarlo y llamar a volver a dibujar. ¿ Pero qué pasa si también
quieres mutar el texto? Entonces, si miro el código,
vemos que, bajo el capó, textView crea una copia
usando una fábrica que se puede expandir. Por lo tanto, tiene una
implementación de fábrica que se puede expandir por defecto. Pero podemos implementar el nuestro. Y luego, en lugar de
crear una copia, simplemente devolveremos la misma
referencia al objeto, en caso de que el objeto
se pueda expandir. Y luego simplemente configuramos
esa fábrica expandible en textView. Entonces, esto es
específicamente importante si estás usando texto de estilo
dentro de un recyclerView.

Porque de esta manera,
evitas crear copias dentro de recyclerView,
ahorrando tiempo de CPU y asignación de memoria. Entonces, en recyclerView, así
es como lo usarías. En viewHolder,
configuraría la fábrica expandible que acaba de crear y luego, en
onBindViewHolder, asegúrese de configurar también el
tipo de búfer como expandible cuando configure el texto. CLARA BAYARRI:
Otra cosa común que vemos relacionada con recyclerView
es el uso de enlace automático. Es posible que esté familiarizado
con tomar una vista de texto y simplemente configurar el
enlace automático para que diga web para poder detectar
URL dentro de su texto y
vincularlas automáticamente.

Bueno, ¿qué sucede bajo
el capó cuando haces esto? Resulta que cuando configuras un
texto en esa vista de texto, primero que nada, crearemos una
copia, tal como dijo Florina, y luego ejecutamos un comparador
para una expresión regular en tu texto. Y para cada coincidencia en la que
encontremos una URL, crearemos un intervalo de URL
y lo agregaremos a su texto. Si hace esto dentro de
onBindViewHolder, estará creando
copias y ejecutando esa expresión regular
cada vez que configura texto, incluso si muestra el
mismo texto en cada elemento. Entonces tienes un pequeño
pie de página con un enlace. Vas a volver a calcular
eso para todos y cada uno de los elementos. Entonces, la solución a esto es una:
no use el enlace automático en su XML, porque eso
desencadena automáticamente todo el proceso; en su
lugar, primero que nada, cree una
copia expandible de su cadena y ejecute Linkify
o LinkifyCompat para poder Encuentre todos
esos enlaces de antemano.

Así que lo calculas todo previamente. Y luego en
onBindViewHolder, simplemente tienes que configurar el texto. Y si utiliza el truco de Florina
de utilizar el tipo de búfer expandible, evitará
todas esas copias y evitará todos
estos cálculos adicionales. En relación con esto, en general,
queremos disuadir a todos de utilizar el mapa de enlace automático. Todas las demás
opciones del enlace automático utilizan expresiones regulares
y son fáciles de ejecutar. Sin embargo, en el mapa en realidad
genera una instancia de una vista web para buscar direcciones, lo que
supone un gran impacto en el rendimiento de su aplicación. Por eso queremos
disuadir a todos de usar map o all,
que incluye map. Y podrías decir,
bueno, pero necesito esto. Y te lo estás quitando. ¿ Qué debo hacer? Smart Linkify viene al rescate
. Quizás recuerdes que el año pasado
presentamos Smart Selection en O, donde utilizamos el aprendizaje automático
para detectar entidades cuando intentabas seleccionar texto. Bueno, este año
tomamos esos mismos modelos de aprendizaje automático que detectan
entidades y los aplicamos a Linkify. De esta manera, podemos lograr una
precisión mucho mayor al detectar todas
las entidades y podemos detectar nuevos tipos.

Entonces, además de los números de teléfono, las
URL y las direcciones, como lo hacemos antes, podemos
hacer cosas más sofisticadas, como códigos de vuelo. La forma en que esto funciona en el código es que
crea una solicitud de enlaces de texto con su texto, y
también puede tomar algunas opciones, luego, en un hilo en segundo plano
, llama a textClassifier.GenerateLinks. Es muy importante que
haga esto en un hilo en segundo plano, ya que se trata de cargar un
modelo de aprendizaje automático desde el disco. Así que no hagas
esto en el hilo de la interfaz de usuario. Una vez que regresa, tiene
todas las entidades detectadas. Puede aplicar esos
enlaces al texto. Aquí hay un cambio entre
el antiguo Linkify y este. El antiguo Linkify solía
generar un intervalo de URL en el que, cuando hacías clic,
simplemente pasaba por el enlace. Y en el caso de Smart
Linkify, al archivar los tramos de URL que se están agregando
aparece una barra de herramientas flotante con
acciones inteligentes que puede realizar. Así, por ejemplo, para una
dirección, podríamos sugerir mapas.

Finalmente, cuando tenga
todo el texto listo, puede volver al hilo de la interfaz de usuario
y configurar ese texto en la vista de texto. Observe que hay una gran diferencia
entre el linkify antiguo y el nuevo, y que
el anterior era sincrónico, y este es asincrónico. Entendemos que esta
es una gran diferencia. Pero es la forma en que podemos
utilizar el aprendizaje automático para mejorar realmente la forma en que detectamos entidades. Y la precisión
de los nuevos modelos es realmente mucho mejor.

Y ya que estoy hablando
de nuevas características, permítanme presentarles
nuestra última característica nueva para P, que es Lupa. Mucha gente nos dice que
seleccionar texto es muy difícil y colocar el cursor donde
quieras es una tarea realmente difícil. Entonces, la Lupa muestra
una versión ampliada de dónde estás
tocando con el dedo para ayudarte a colocar el cursor
en la posición correcta. Hemos implementado esto de forma
predeterminada en TextView, edición de texto y en la vista web en Chrome. Por lo tanto, no tiene que
hacer ningún trabajo si usa los widgets predeterminados. Sin embargo, si está creando
sus propios widgets personalizados y desea
implementarlos usted mismo, existe una API muy fácil
de usar. Podrías hacer
algo como esto, simplemente toma tu evento intacto. Y cada vez que el
dedo baje, muestre la lupa,
cada vez que el dedo suba, descarte la lupa. Esto crea un efecto en el que
la lupa sigue el dedo por la pantalla. Esta no es la
UX final que hemos elegido. En cierto modo nos ajustamos a las líneas. Publicaremos
pautas sobre la UX final que hemos creado, pero
es muy fácil de usar.

Espero que hoy hayamos presentado
un montón de consejos y trucos. Le mostramos qué texto hay
debajo del capó en Android y le brindamos
más información sobre cómo diseñar su
texto para que pueda llevar todo esto
a sus aplicaciones y crear
aplicaciones hermosas y con mayor rendimiento. [REPRODUCIENDO MÚSICA].

As found on YouTube