Android

18 mar, 2016

Views customizadas no Android – Parte 02

Publicidade

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

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>
MoneyView – Valor negativo com cor e tamanho customizado
MoneyView – Valor negativo com cor e tamanho customizado

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.