sexta-feira, 30 de novembro de 2012

Criando Views

Olá pessoal,

Depois de algum tempo venho falar sobre a criação de views e encerrar uma sequência de 3 posts relacionados a este assunto. Nos posts anteriores mostrei como customizar uma View e também como criar uma View composta de outras Views. Neste post vou mostrar como criar uma View a partir da extensão da classe android.view.View.

A View criada neste post não terá funcionalidade, será apenas um desenho multi direcional, pois vou abordar em um post mais a frente como dar vida a este tipo de controle utilizando o acelerômetro do dispositivo. Este post servirá como base para o conhecimento da estrutura de uma View criada do zero.

No final nossa View ficará como a imagem abaixo:


Aproveito este post para fazer um convite a vocês a instalar a versão Jelly Bean 4.1.2 de testes da Samsung. Está fantástica. Veja sobre no site SamMobile.com

O primeiro passo para se construir uma View do zero é criar uma classe fazer a extensão da classe android.view.View. Fazendo isso, você está herdando todos os comportamentos básicos de uma View. O ponto seguinte é se preocupar me sobrecarregar todos os construtores da classe.

public class DirectionalView extends View



public DirectionalView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initDirectionalView();

}

public DirectionalView(Context context, AttributeSet attrs) {
super(context, attrs);
initDirectionalView();
}

public DirectionalView(Context context) {
super(context);
initDirectionalView();
}


Repare que todos o três construtores fazem chamada ao initDirectionalView. Este procedimento é utilizando para inicializar as variáveis que serão utilizadas pelo sistema. É uma boa prática fazer a inicialização das várias logo no início do aplicativo, assim você não evita de executar uma ação de inicialização várias vezes desnecessariamente, lembre-se é importante se preocupar com o processo durante a execução de um aplicativo móvel.

O procedimento initDirectionalView está definindo que o controle não irá receber foco, definindo os objetos que irão desenhar as linhas na tela, carregando os textos dos Resources para exibição em tela e definindo a cor da tela baseando se nos Resources.


private void initDirectionalView()
{
if (this.isInEditMode())
return;

// esta ação define que o validador não receberá foco...
setFocusable(false);

// referencia todos os itens...
Resources r = this.getResources();

linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
linePaint.setColor(r.getColor(R.color.borderColor));
linePaint.setStyle(Paint.Style.FILL);
linePaint.setStrokeWidth(1);

textHeight = 20;
textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
textPaint.setColor(r.getColor(R.color.foreColor));
textPaint.setTextSize(textHeight);



textEast = r.getString(R.string.east);
textNorth = r.getString(R.string.north);
textSouth = r.getString(R.string.south);
textWest = r.getString(R.string.west);

this.setBackgroundColor(r.getColor(R.color.backcolor));

}

Até o momento descrevi apenas como a View é inicializada, agora precisamos ver como a View é desenhada. O desenho da View é feito dentro da chamada onDraw, que recebe como parâmetro o Canvas, que é o local onde fazemos o desenho da View. Esta chamada é feita sempre que for necessário redesenhar a tela.


@Override
protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

/* define os valores que podem virar parametros*/
int lineMargin = 40;
int textMargin = 20;

int height = this.getHeight();
int width = this.getWidth();

/* encontra os centros */
int centerX = width/2 ;
int centerY = height/2;

/* define os pontos*/
int topX = centerX;
int topY = lineMargin;

int bottomX = centerX;
int bottomY = height - lineMargin;

int leftX = lineMargin;
int leftY = centerY;

int rightX = width - lineMargin;
int rightY = centerY;

int topLeftX = width / 3;
int topLeftY = height /3;

int bottomRightX = (width * 2)/3;
int bottomRightY = (height * 2)/3;

int topRightX = (width * 2)/3;
int topRightY = height/3;

int bottomLeftX = width / 3 ;
int bottomLeftY =  (height * 2)/3;

/* desenha as linhas principais */
canvas.drawLine(topX, topY,bottomX ,bottomY, linePaint);
canvas.drawLine(leftX, leftY,rightX ,rightY, linePaint);
canvas.drawLine(topLeftX, topLeftY,bottomRightX ,bottomRightY, linePaint);
canvas.drawLine(topRightX, topRightY, bottomLeftX, bottomLeftY, linePaint);

/* desenha as linhas secundárias */
canvas.drawLine(topX, topY, topLeftX, topLeftY, linePaint);
canvas.drawLine(topLeftX, topLeftY, leftX, leftY, linePaint);
canvas.drawLine(leftX, leftY, bottomLeftX, bottomLeftY, linePaint);
canvas.drawLine(bottomLeftX, bottomLeftY, bottomX, bottomY, linePaint);
canvas.drawLine(bottomX, bottomY, bottomRightX, bottomRightY, linePaint);
canvas.drawLine(bottomRightX, bottomRightY, rightX, rightY, linePaint);
canvas.drawLine(rightX, rightY, topRightX, topRightY, linePaint);
canvas.drawLine(topRightX, topRightY, topX, topY, linePaint);

canvas.drawText(textNorth, topX, textMargin, textPaint);
canvas.drawText(textSouth, bottomX,bottomY + textMargin+textHeight, textPaint);
canvas.drawText(textEast, rightX + textHeight, rightY, textPaint);
canvas.drawText(textWest, textMargin, leftY, textPaint);


}



Repare no código acima que a partir do canvas todas as linhas são feitas e os textos escritos.

Outro ponto importante é o redimensionamento da View. Todas as vezes que a View for redimensionada é necessário verificar o tamanho e reaplicar o redimensionamento. Repare que propositalmente eu criei uma função que verifica o tamanho retornado pelo chamada onMeasure e atribui o tamanho. Se não tiver um tamanho especificado utiliza 400dp.


@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {


/*captura as medidas de altura e largura...*/ 
int measuredWidth = measure(widthMeasureSpec);
int measuredHeight = measure(heightMeasureSpec);

/*define a medida baseando-se na menor*/
int d = Math.min(measuredWidth, measuredHeight);
setMeasuredDimension(d, d);

}

private int measure(int measureSpec) {

/* verifica o modo de medida e se não tiver tamanho define o valor 200 como padrão */
int mode = MeasureSpec.getMode(measureSpec);
int size = MeasureSpec.getSize(measureSpec);

if (mode == MeasureSpec.UNSPECIFIED)
size = 400;

return size;
}

A utilização da View é do mesmo formato que defini nos posts anteriores. Mas basta relembrar:


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" 
    android:background="@color/backcolor"
    
    >
       <com.example.criandoviews.DirectionalView 
        android:id="@+id/directional1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true" 
        />

</RelativeLayout>

Pessoal este foi o último post em português, este blog está passando por reestruturação e o próximo post será em inglês. Espero que tenham gostado.

O download do código fonte pode ser feito clicando aqui.

Abraço