No artigo anterior, mostramos como criar views customizadas a partir da extensão de widget e do agrupamentos de views. Hoje, veremos como é possível definir atributos customizados e utilizá-los direto do arquivo de layout que possui a view criada.
Atributos customizados
Para possibilitar que uma view seja fácil de usar e flexível, é possível criar atributos que podem ser utilizados para configurar/customizar uma view. Para exemplificar, vamos utilizar a MoneyView, que foi criada na primeira parte do artigo.
Na view correspondente aos valores monetários, podemos criar atributos para definir o tamanho do texto, se a cor do texto deve ser modificada quando o valor for negativo e qual cor deve ser utilizada.
Criação dos atributos
Os atributos são declarados por meio do recurso declare-styleable, que suporta a configuração de diversos atributos. Normalmente, estes recursos são inseridos no arquivo @res/values/attrs.xml.
O Android permite que o atributo seja definido com os tipos abaixo:
- integer – números inteiros (também suporta declaração hexadecimal)
- float – números decimais
- fraction – percentagem, n% ou n%p (relativo ao elemento-pai), onde n deve ser um número inteiro.
- boolean – booleano (true/false)
- reference – referência ao resource (drawables, strings, colors, layouts, etc)
- color – referência a uma cor
- dimension – referência a uma dimensão (dp, sp, pt, px, mm, in)
- string – um texto ou referência a uma string
- enum – conjunto de valores que são aceitos
Agora que já sabemos como declarar os aspectos customizáveis, vamos à implementação.
MoneyView
O primeiro passo será criar no arquivo attrs.xml os atributos: “mv_currencySize” (tamanho da fonte da moeda), “mv_valueSize” (tamanho da fonte do valor monetário), “mv_defaultColor” (cor padrão do texto), “mv_negativeColor” (cor do texto quando o valor for negativo), “mv_value” (valor monetário) e “mv_enableChangeColor” (habilitação da mudança de cor quando o valor for negativo) da MoneyView.
Para melhor organização de código, é recomendado utilizar como prefixo as iniciais da classe correspondente à view customizada, pois em um projeto é comum existir diversos atributos customizados e, desta forma, conseguimos identificar facilmente a qual view aquele atributo pertence.
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="MoneyView"> <attr name="mv_currencySize" format="dimension" /> <attr name="mv_valueSize" format="dimension" /> <attr name="mv_defaultColor" format="color" /> <attr name="mv_negativeColor" format="color" /> <attr name="mv_value" format="string" /> <attr name="mv_enableChangeColor" format="boolean" /> </declare-styleable> </resources>
O segundo passo é adicionar no construtor da view customizada a obtenção dos valores passados nos atributos.
Essa operação é realizada com o TypedArray, que consiste em um container para o array dos parâmetros da view customizada. O TypedArray é obtido pelo método obtainStyledAttributes da classe Context do Android.
Um ponto de atenção é chamar o método recycle do TypeArray para que ele seja liberado e possa ser reutilizado posteriormente.
@MoneyView.java
package com.tpinho.customviews.ui.custom; import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; import android.support.annotation.StringRes; import android.support.v4.content.ContextCompat; import android.text.TextUtils; import android.util.AttributeSet; import android.util.TypedValue; import android.widget.LinearLayout; import android.widget.TextView; import com.tpinho.customviews.R; import java.math.BigDecimal; import br.com.concretesolutions.canarinho.formatador.FormatadorValor; import static android.os.Build.VERSION_CODES.LOLLIPOP; /** * Created by tpinho on 1/4/16. */ public class MoneyView extends LinearLayout { private float currencySize; private float valueSize; private int defaultColor; private int negativeColor; private boolean enableChangeColor; private String value; private TextView textMoneyCurrency; private TextView textMoneyValue; public MoneyView(Context context) { super(context); init(); } public MoneyView(Context context, AttributeSet attrs) { super(context, attrs); initWithStyle(context, attrs); } public MoneyView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initWithStyle(context, attrs); } @TargetApi(LOLLIPOP) public MoneyView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); initWithStyle(context, attrs); } private void initWithStyle(final Context context, final AttributeSet attrs) { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MoneyView); currencySize = typedArray.getDimensionPixelSize(R.styleable.MoneyView_mv_currencySize, getResources().getDimensionPixelSize(R.dimen.dimen_text_15_sp)); valueSize = typedArray.getDimensionPixelSize(R.styleable.MoneyView_mv_valueSize, getResources().getDimensionPixelSize(R.dimen.dimen_text_15_sp)); defaultColor = typedArray.getColor(R.styleable.MoneyView_mv_defaultColor, ContextCompat.getColor(getContext(), R.color.black)); negativeColor = typedArray.getColor(R.styleable.MoneyView_mv_negativeColor, ContextCompat.getColor(getContext(), R.color.red)); value = typedArray.getString(R.styleable.MoneyView_mv_value); enableChangeColor = typedArray.getBoolean(R.styleable.MoneyView_mv_enableChangeColor, Boolean.TRUE); typedArray.recycle(); init(); } private void init() { inflate(getContext(), R.layout.view_money, this); textMoneyCurrency = (TextView) findViewById(R.id.text_money_currency); textMoneyCurrency.setTextSize(TypedValue.COMPLEX_UNIT_PX, currencySize); textMoneyValue = (TextView) findViewById(R.id.text_money_value); textMoneyValue.setTextSize(TypedValue.COMPLEX_UNIT_PX, valueSize); setCurrency(R.string.default_currency); textMoneyCurrency.setTextColor(defaultColor); textMoneyValue.setTextColor(defaultColor); if (TextUtils.isEmpty(value)) { setValue(R.string.default_value); } else { setValue(value); } } public void setCurrency(@StringRes int text) { textMoneyCurrency.setText(text); } public void setCurrency(String text) { textMoneyCurrency.setText(text); } public void setValue(@StringRes int value) { setValue(getResources().getString(value)); } public void setValue(String value) { textMoneyValue.setText(FormatadorValor.VALOR.formata(value)); if (enableChangeColor) { final int color; if (getValue().compareTo(BigDecimal.ZERO) < 0) { color = negativeColor; } else { color = defaultColor; } textMoneyCurrency.setTextColor(color); textMoneyValue.setTextColor(color); } } public BigDecimal getValue() { return new BigDecimal(FormatadorValor.VALOR.desformata(textMoneyValue.getText().toString())); } }
Para utilizar os atributos customizados no arquivo de layout (xml), é necessário declarar um namespace que será utilizado para referenciar os atributos, como: xmlns:app=”http://schemas.android.com/apk/res-auto”.
@activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical"> <com.tpinho.customviews.ui.custom.MoneyView android:id="@+id/money_main_price" android:layout_width="wrap_content" android:layout_height="wrap_content" app:mv_value="50000"/> </LinearLayout>

No exemplo acima foi definido apenas um atributo customizado: o valor, por meio do atributo mv_value. Assim, os outros atributos da view continuarão com seus valores predefinidos.
Também é possível utilizar todos os atributos correspondentes à view. Como podemos observar abaixo, serão definidos cor padrão, valor, tamanho do texto e cor do texto quando o valor é negativo.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical"> <com.tpinho.customviews.ui.custom.MoneyView android:id="@+id/money_main_price" android:layout_width="wrap_content" android:layout_height="wrap_content" app:mv_defaultColor="#3C3C3C" app:mv_value="-15000" app:mv_valueSize="30sp" app:mv_currencySize="18sp" app:mv_negativeColor="#FFCF0F"/> </LinearLayout>

E é isso! O projeto de exemplo pode ser encontrado neste repositório do Github.
Ficou alguma dúvida ou tem alguma sugestão? Aproveite os campos abaixo!
***
Esse artigo foi originalmente publicado no Medium e no blog pessoal do autor.