quarta-feira, 16 de maio de 2012

Convertendo pixels em dp e vice-versa

( Android )


Eu tive alguns problemas com o tamanho dos objetos customizados no android utilizando a API do Java em telas com densidades diferentes, e vou demonstrar como resolver esse problema, uma vez que o Java utiliza pixels como unidade de medida e não dp.  Mas antes vou falar um pouco de tamanho de tela (screen size). 


Essa medida refere-se ao tamanho físico da tela, medida na diagonal.  O  Android atualmente separa os tamanhos físicos de tela em quatro diferentes categorias, que são:  tela pequena small, tela normal normal, tela grande large, tela extragrande xlarge. 


Vou dar um exemplo mais claro de cada nomenclatura com aparelhos reais. 


small:  Sony Ericsson, Xperia Mini,  que possuem uma pequena tela QVGA de 240 x 320 pixels 


normal: HTC G1, HTC Magic , Motorola DEXT, possuem uma tela HVGA média 320 x 480 pixels. Nesse grupo também se encontram os celulares um pouco maiores como o Nexus One, Sony  Xperia X10, Mororola Milestone, Samsung Galaxy S, que possuem telas WVGA que varia entre 480 x 800 e 480 x 854 pixels. 


large: O tablet Samsung Galaxy Tab de 7", que possui tela WVGA de 1024 x 600 pixels 


xlarge: Os novos tablets com Andoids 3.x, com tela WXGA de 1280 x 800 pixels 




Obs: Essas medidas de tela são aproximadas e podem ter variações. 


A resolução da tela é expressa em pixels, por exemplo, 320 x 480 px. Diferente do Iphone, no Android temos diversos celulares ,com  resoluções diferentes, o que torna a vida do desenvolvedor android mais emocionante  ( Risos ).  Assim, no desenvolvimento dos aplicativos devemos evitar utilizar essa notação para definir os tamanhos das views, valores de margens, espaçamentos e qualquer outro parâmetro. No Android nunca trabalhamos diretamente com pixels, e sim com dp (density-independent pixel). 


Densidade da tela é basicamente a relação entre a quantidade de pixels existentes na tela com a sua largura e altura. Quanto maior a densidade, maior será a quantidade de pixels espalhados pela tela. 


Bem, agora é fácil, é só eu usar dp ao invés de px que todos os meus problemas estão resolvidos. 


Mas e se você criar uma view customizada com o Canvas ao invés do xml por exemplo ? 


Como eu havia explicado anteriormente, o Java utiliza a notação de pixels para definir valores. 




Então chega de falar e vamos ao código. 


Digamos que estamos criando uma view customizada e precisamos desenhar um quadrado na tela. Para isso precisamos utilizar a API de desenho. Vamos lá. 


1 - Crie um projeto android no eclipse. 


2 - Crie uma classe TesteView para API de desenho. 




print do projeto. 




O código da Activity é simples, vamos apenas alterar o setContentView(); 


public class ExemploConverPixelDP extends Activity {
    

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new TesteView(this));
    }
    
   
    
}

Segue o código da classe TesteView. 

public class TesteView extends View {



private Paint paint;



public TesteView(Context context) {

super(context);



paint = new Paint();



paint.setColor(Color.LTGRAY);
paint.setStyle(Style.FILL);

}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

canvas.drawRect(0, 0, 320 / 2, 480 / 2, paint);

}

}

Rode e veja o resultado. 



Nós sabemos que existem telas HVGA de 320 x 480 pixels e, portanto o quadrado vai ocupar a metade do tamanho da tela. Mas o que acontece se executarmos o mesmo exemplo em uma tela de maior densidade, como uma WVGA de 480 x 800 pixels. 


Segue o print do exemplo executado no virtual divice com a tela HVGA de 320 x 480 pixels a esquerda, e a direita a tela WVGA de 480 x 800 pixels. 




Como podemos verificar, na tela HVGA, o exemplo funcionou como o esperado, e nosso quadrado que (na verdade é um retângulo) preenche exatamente a metade da tela. Isso porque colocamos o valor fixo em pixels no método de desenho.  Mas ao executarmos o mesmo exemplo em um aparelho de densidade mais alta o resultado foi diferente. O "quadrado" foi reduzido, pois a quantidade de pixels é maior nessa tela. E agora, o que fazemos ? 


A solução é pensar que esses valores de 320 x 480 pixels na verdade estão em dp ( density-independent pixel) e fazer a conversão do valor de dp para pixels.  Se fosse um arquivo XML de layout bastava colocar a notação em (dp) que tudo ficava lindo, mas em Java teremos que fazer a conversão manualmente. 


Método que converte os valores: 



public float toPixel(Context context, float dip) {
Resources r = context.getResources();
float densidade = r.getDisplayMetrics().density;
int px = (int) (dip * densidade + 0.5f);
return px;
}


Segue o código completo: 




public class TesteView extends View {

private Paint paint;

public TesteView(Context context) {
super(context);

paint = new Paint();

paint.setColor(Color.LTGRAY);
paint.setStyle(Style.FILL);

}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

// valores em dp
float larguraDP = 320;
float alturaDP = 480;

// convertendo para pixels
float larguraPX = toPixel(getContext(), larguraDP);
float alturaPX = toPixel(getContext(), alturaDP);

// desenha o quadrado
canvas.drawRect(0, 0, larguraPX / 2, alturaPX / 2, paint);

}

// converte de DP para Pixels
public float toPixel(Context context, float dip) {
Resources r = context.getResources();
float densidade = r.getDisplayMetrics().density;

int px = (int) (dip * densidade + 0.5f);
return px;
}

}



Agora rode e olhe o resultado. 


O nosso quadrado (retângulo) agora ocupa o mesmo espaço, nas duas talas com diferentes densidades. 


print do virtual device:  





 Eu esqueci de mencionar !!! 


tela QVGA retorna a densidade de 0.75 
tela HVGA retorna a densidade de 1.0 
tela WVGA retorna a densidade de 1.5




 Problema resolvido.  




Até mais ! 






Nenhum comentário:

Postar um comentário