Como é o seu texto?

Amigos, ótimo toda sexta-feira. Queremos compartilhar com você a tradução de um artigo preparado especialmente para os alunos do curso “Android-developer. Curso avançado . Boa leitura.



Como declarativamente estilizar o texto no Android.


Ilustração de Virginia Poltrack

TextView em aplicativos Android fornece vários atributos para estilizar o texto e várias maneiras de aplicá-lo. Esses atributos podem ser configurados diretamente no layout, aplicar um estilo à visualização ou tema ao layout ou, se você desejar, definir textAppearance. Mas o que isso deve ser usado? E o que acontece se você combiná-los?


O que e quando usar?

Este artigo descreve várias abordagens para estilização declarativa de texto (ou seja, quando você define estilos em um arquivo XML), discute o escopo e a prioridade de cada método.

tl; dr;


Você deve ler o post inteiro, mas aqui está um resumo.

Lembre-se da ordem de prioridade dos vários métodos de estilo - se você estiver tentando estilizar algum texto e não encontrar os resultados esperados, provavelmente suas alterações serão substituídas por algo com uma prioridade mais alta nesta hierarquia:


Hierarquia de métodos de estilo de texto

Eu sugeriria o seguinte procedimento para estilizar:

  1. Defina qualquer estilo de aplicativo no textViewStyle como o estilo padrão para o seu tema.
  2. Instale o conjunto (pequeno) de TextAppearance que seu aplicativo usará (ou usará / herdará dos estilos MaterialComponent) e faça referência a eles diretamente da sua exibição
  3. Crie um style definindo atributos não suportados pelo TextAppearance (que eles mesmos determinarão um dos seus TextAppearance ).
  4. Execute qualquer estilo exclusivo diretamente no layout.

Mostre algum estilo


Você pode definir diretamente os atributos do TextView no layout, mas essa abordagem pode ser mais entediante e insegura. Imagine que dessa maneira você está tentando atualizar a cor de todos os TextViews no aplicativo. Como em todas as outras visualizações, você pode (e deve!) Usar estilos para garantir consistência, reutilização e facilidade de atualização. Para esse fim, recomendo criar estilos para texto sempre que você desejar aplicar o mesmo estilo a várias visualizações. Isso é extremamente simples e amplamente suportado pelo sistema de exibição do Android.

O que acontece quando você estiliza a vista? Se você já escreveu sua exibição personalizada, provavelmente viu a chamada para context.obtainStyledAttributes (AttributeSet, int [], int, int) . Assim, o sistema de visualização no Android passa para a visualização os atributos especificados no layout. O parâmetro AttributeSet , de fato, pode ser considerado como um mapa dos parâmetros XML que você especifica em seu layout. Se o AttributeSet definir o estilo, o estilo será lido primeiro e, em seguida, os atributos especificados diretamente na exibição serão aplicados a ele. Assim, chegamos à primeira regra de prioridade.

Exibir → Estilo

Os atributos definidos diretamente na visualização sempre "prevalecem" e substituem os atributos definidos no estilo. Observe que uma combinação de atributos de estilo e exibição é aplicada; definir um atributo na exibição, que também é especificado no estilo, não cancela o estilo inteiro. Note-se também que, na sua opinião, não existe uma maneira real de determinar de onde vem a estilização; Isso é decidido pelo sistema de visualização para você uma vez em uma chamada semelhante. Você não pode obter as duas opções e escolher.

Embora os estilos sejam extremamente úteis, eles têm suas limitações. Uma delas é que você pode aplicar apenas um estilo à visualização (em oposição a algo como CSS, onde você pode aplicar várias classes). TextView , no entanto, tem um truque: fornece o atributo TextAppearance , que funciona de maneira semelhante ao style . Se você style texto por meio do TextAppearance , deixe o atributo style livre para outros estilos, o que parecerá prático. Vamos dar uma olhada no que TextAppearance e como ele funciona.

Textappearance


Não há nada mágico no TextAppearance (por exemplo, um modo secreto para aplicar vários estilos que você não deveria saber !!!!), o TextView evita que você trabalhe desnecessariamente. Vamos dar uma olhada no construtor TextView para entender o que está acontecendo.

 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); 

Então, o que está acontecendo aqui? Basicamente, o TextView primeiro verifica se você especificou android:textAppearance ; android:textAppearance caso, ele carrega esse estilo e aplica todas as propriedades listadas lá. Mais tarde, ele carrega todos os atributos da visualização (que ele lembra, incluindo o estilo) e os aplica. Então chegamos à segunda regra de prioridade:

Exibir → Estilo → Aparência de texto

Como a aparência do texto é marcada primeiro, qualquer atributo definido diretamente na exibição ou no estilo o substituirá.

Com o TextAppearance , há outra TextAppearance : ele suporta um subconjunto dos atributos de estilo que o TextView oferece. Para entender melhor o que quero dizer, vamos voltar a esta linha:

obtainStyledAttributes(ap, android.R.styleable.TextAppearance);

Vimos a versão receiveStyledAttributes com 4 argumentos, essa versão com 2 argumentos é um pouco diferente dela. Ela analisa o estilo fornecido (conforme definido pelo primeiro id parâmetro) e o filtra apenas de acordo com os atributos no estilo que aparecem no segundo parâmetro, o array attrs . O android.R.styleable.TextAppearance tão estiloso que define o escopo do TextAppearance . Observando essa definição, vemos que o TextAppearance suporta muitos, mas não todos, atributos que o TextView suporta .

 <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 suportados pelo TextAppearance

Aqui estão alguns atributos do TextView que não estão incluídos no TextAppearance : lineHeight[Multiplier|Extra] , lines , breakStrategy e breakStrategy . TextAppearance funciona no nível do caractere, não no nível do parágrafo; portanto, os atributos que afetam o layout inteiro não são suportados.

Portanto, TextAppearance muito útil, pois permite definir um estilo orientado para os atributos de estilização de texto e deixa o style à vista livre para outros fins. No entanto, ele tem um escopo limitado e está na parte inferior da cadeia de prioridades, portanto, não se esqueça das limitações.

Padrões razoáveis


Quando analisamos como a visualização Android resolve atributos ( context.obtainStyledAttributes ), na verdade simplificamos um pouco. Ela chama theme.obtainStyledAttributes (usando o Theme Context atual 'a). Ao verificar o link , a ordem de prioridade que examinamos anteriormente é mostrada e mais 2 locais são indicados que ele está procurando para resolver atributos: o estilo padrão da visualização e o tema.

Ao determinar o valor final de um atributo específico, quatro parâmetros de entrada entram em jogo:

  1. Qualquer valor de atributo neste AttributeSet.
  2. O recurso de estilo especificado no AttributeSet (chamado "style").
  3. O estilo padrão especificado por defStyleAttr e defstyleres
  4. Valores básicos neste segmento.

Ordem de prioridade de estilo a partir da documentação do tema

Voltaremos aos temas, mas vamos dar uma olhada nos estilos padrão primeiro. Qual é esse estilo padrão? Para responder a essa pergunta, acho que seria útil fazer uma pequena saída do tema TextView e dar uma olhada em um simples Button . Quando você insere < Button > no layout, ele se parece com isso.


Botão Padrão

Porque Se você olhar o código fonte do Button , verá que ele é bastante escasso:

 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); } } 

Isso é tudo! Aqui está toda a classe (sem comentários). Você pode conferir você mesmo aqui . Vou esperar Então, de onde vêm os antecedentes, letras maiúsculas, ondulações etc.? Você pode ter perdido, mas tudo será definido no construtor com 2 argumentos; aquele que é chamado quando o layout é obtido do XML. Este é o último parâmetro que define defaultStyleAttr em com.android.internal.R.attr.buttonStyle . Este é o estilo padrão, que é essencialmente um ponto de referência indireto, permitindo que você especifique o estilo padrão. Ele não aponta diretamente para o estilo, mas permite que você aponte para um dos especificados em seu tópico que ele verificará ao resolver atributos. E é exatamente isso que todos os temas dos quais você geralmente herda fazem para fornecer a aparência dos widgets padrão. Por exemplo, se você examinar o tópico Material, ele define @style/Widget.Material.Light.Button , e é esse estilo que fornece todos os atributos que theme.obtainStyledAttributes se você não especificou mais nada.

De volta ao TextView , ele também oferece um estilo padrão: textViewStyle . Isso pode ser muito conveniente se você deseja aplicar alguns estilos a cada TextView no seu aplicativo. Suponha que você queira definir o espaçamento de linha padrão como 1,2. Você pode fazer isso com style/TextAppearance e tentar aplicá-lo durante uma revisão de código (ou talvez até com uma regra personalizada elegante no Lint), mas você precisa estar atento e recrutar novos membros da equipe , tenha cuidado com a refatoração, etc.

Uma abordagem melhor seria especificar seu próprio estilo padrão para todos os TextView no aplicativo, definindo o comportamento desejado. Você pode fazer isso definindo seu próprio estilo para textViewStyle , que é herdado da plataforma ou de MaterialComponents/AppCompat por padrão.

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

Com isso em mente, nossa regra de prioridade assume a forma:

Ver -> Estilo -> Estilo Padrão -> Aparência de Texto

Como parte da resolução dos atributos do sistema de exibição, esse espaço é preenchido após os estilos (para que tudo no estilo seja cancelado pelo estilo aplicado ou pelos atributos de exibição por padrão), mas ainda substitua a aparência do texto. Os estilos padrão podem ser muito convenientes. Se você decidir escrever sua própria exibição personalizada, ela poderá ser uma maneira poderosa de implementar o comportamento padrão, facilitando a personalização.

Se você herdar o widget e não especificar seu próprio estilo padrão, use o estilo de classe pai padrão nos construtores (não passe apenas 0). Por exemplo, se você herdar de AppCompatTextView e gravar seu próprio construtor com 2 argumentos, passe android.R.attr.textViewStyle defaultStyleAttr ( como aqui ), caso contrário você perderá o comportamento da classe pai.

Temas


Como mencionado anteriormente, existe outra (última, prometo) maneira de fornecer informações sobre o estilo. Outro theme.obtainStyledAttributes abordará diretamente o próprio tópico. Ou seja, se você adicionar um atributo de estilo ao seu tema, por exemplo android:textColor , o sistema de exibição o selecionará como último recurso. Como regra, é uma má idéia misturar atributos de tema e atributos de estilo, ou seja, o que você aplica diretamente à exibição, como regra, nunca deve ser definido para o tema (e vice-versa), mas existem algumas exceções raras.

Um exemplo seria quando você tenta alterar a fonte em todo o aplicativo. Você pode usar um dos métodos descritos acima, mas o ajuste manual dos estilos / aparência do texto em qualquer lugar será monótono e inseguro, e os estilos padrão funcionarão apenas no nível do widget; As subclasses podem substituir esse comportamento; por exemplo, os botões definem seu próprio android:buttonStyle , que seu android:textViewStyle não atenderá. Em vez disso, você pode especificar a fonte no seu tema:

 <style name="Theme.MyApp" parent="@style/Theme.MaterialComponents.Light"> ... <item name="android:fontFamily">@font/space_mono</item> </style> 

Agora, qualquer visualização que suporte esse atributo o buscará se não for substituída por algo com uma prioridade mais alta:

Exibir → Estilo → Estilo padrão → Tema → Aparência de texto

Novamente, como isso faz parte do sistema de estilo de exibição, ele substituirá tudo o que é fornecido em forma de texto, mas será substituído por quaisquer atributos mais específicos.

Lembre-se desta prioridade. No nosso exemplo, com uma fonte para todo o aplicativo, você pode esperar que a Toolbar escolha essa fonte, pois ela contém o título, que é um TextView . A própria classe Barra de Ferramentas, no entanto, define um estilo padrão que contém titleTextAppearance , que define android:fontFamily , e o define diretamente no cabeçalho do TextView , substituindo o valor do nível do tema. Os estilos no nível do tópico podem ser úteis, mas fáceis de substituir, portanto, verifique se eles foram aplicados corretamente.

Bônus: problemas não resolvidos


Este artigo inteiro foi dedicado ao design declarativo do texto no nível da visualização, ou seja, como estilizar todo o TextView durante o preenchimento. Qualquer estilo aplicado após o preenchimento (por exemplo, textView.setTextColor(…) ) substituirá o estilo declarativo. TextView também suporta estilos menores através do Span . Não vou entrar neste tópico, como é descrito em detalhes nos artigos de Florina Muntenescu .

Estilo de texto fantasmático com vãos
Vãos compreensivos

Mencionarei isso por questões de integridade, tendo em mente que o estilo e a extensão do programa estarão no topo da ordem de prioridade:

Extensão → Setters → Exibir → Estilo → Estilo padrão → Tema → Aparência de texto

Escolha o seu estilo


Embora existam várias maneiras de estilizar seu texto, entender as diferenças entre os métodos e suas limitações ajuda a encontrar a ferramenta certa para uma tarefa específica ou a entender por que um método tem precedência sobre outro.

Tenha um bom estilo de texto!

Convidamos a todos para um webinar gratuito no âmbito do qual conheceremos a estrutura DI Dagger 2: estudaremos como o Dagger2 gera código, lidaremos com as anotações JSR 330 e as construções Dagger2, aprenderemos como usar o Dagger2 em um aplicativo multi-módulo e consideraremos o Dagger Android Injector.

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


All Articles