Fórmula px para dp, dp para px android


150

Estou tentando calcular uma quantidade variável de pixels para densificar pixels independentes e vice-versa.

Essa fórmula (px to dp): dp = (int)(px / (displayMetrics.densityDpi / 160));não funciona em dispositivos pequenos porque é dividida por zero.

Esta é minha fórmula de dp para px:

px = (int)(dp * (displayMetrics.densityDpi / 160));

Alguém poderia me dar algumas dicas?


1
Convertendo unidades dp para unidades de pixel developer.android.com/guide/practices/...
— Padma Kumar

@Ram: Acho que sua fórmula está ótima. Como você obterá uma divisão por zero? O displayMetrics.densityDpi será 120, 160, 240 ou 320, nunca 0.
— ct_rob

Eu concordo com @ct_rob. O valor mínimo do displayMetrics.densityDpi / 160 será 0,75. Você deve ter transmitido para int no local incorreto.
— TomTaila

Respostas:


318

Nota: A solução amplamente usada acima é baseada em displayMetrics.density. No entanto, os documentos explicam que esse valor é um valor arredondado, usado com a tela 'buckets'. Por exemplo. no meu Nexus 10, ele retorna 2, onde o valor real seria 298dpi (real) / 160dpi (padrão) = 1,8625.

Dependendo dos seus requisitos, você pode precisar da transformação exata, que pode ser alcançada assim:

[Editar] Isso não deve ser misturado com a unidade de DP interna do Android, pois isso ainda é baseado nos baldes de tela. Use-o onde desejar uma unidade que renderize o mesmo tamanho real em diferentes dispositivos.

Converter dp em pixel:

public int dpToPx(int dp) {
    DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
    return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));     
}

Converter pixel em dp:

public int pxToDp(int px) {
    DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
    return Math.round(px / (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
}

Observe que existem propriedades xdpi e ydpi, você pode querer distinguir, mas não consigo imaginar uma exibição sadia em que esses valores sejam muito diferentes.


8
Isso não calculará o valor correto para dp / px em muitos dispositivos (incluindo o Nexus 10)! Como você diz displayMetrics.density, é arredondado para o intervalo de tela mais próximo, mas a unidade dp! Tente desenhar um objeto com 160dp de largura e logo abaixo dele, você desenha outro objeto com dpToPx (160) pixels de largura e verá que o tamanho dos dois objetos é diferente. Além disso, alguns telefones (como o Galaxy Mini e o Galaxy S3 Mini) relatam valores completamente errados para xdpi / ydpi; portanto, nesses telefones, seus métodos retornam resultados completamente errados.
— Nibarius

@nibarius Sim, você não pode misturar os cálculos de dp em caixa do Android com os itens acima. A solução acima é entendida como um valor independente da densidade separada, com base na física exata do dispositivo. Necessário, por exemplo, onde você deseja mostrar uma linha exatamente o mesmo comprimento real em diferentes dispositivos. Obviamente, se as entradas xdpi / ydpi não forem definidas corretamente por alguns dispositivos, elas não funcionarão lá.
— Bachi

1
xdpi e ydpi não devem ser usados, porque são imprecisos em muitos dispositivos, às vezes muito. Somente o DisplayMetrics.densityDpi é confiável, o que é lamentável, pois é impreciso por design. Consulte o tópico do fórum do Google para obter mais informações: groups.google.com/forum/#!topic/android-developers/g56jV0Hora0
— Mark McClelland

1
Encontrei essa resposta e, na verdade, usei muitas soluções semelhantes, mas apenas analisei os documentos e encontrei getDimensionPixelOffSet com uma determinada dimensão (declarada em dip / dp) que retorna o deslocamento em pixels, assim como o código artesanal. Eu testei e trabalhei na perfeição. Espero que ajude!
— william Gouvêa

1
Mehhh, isso não dá o valor correto. Tente: Recursos r = getResources (); px flutuante = TypedValue.applyDimension (TypedValue.COMPLEX_UNIT_DIP, 14, r.getDisplayMetrics ()); Conforme descrito aqui: stackoverflow.com/questions/4605527/converting-pixels-to-dp
— Rik van Velzen

103

Resolvi meu problema usando as seguintes fórmulas. Que outras pessoas possam se beneficiar disso.

dp para px:

displayMetrics = context.getResources().getDisplayMetrics();
return (int)((dp * displayMetrics.density) + 0.5);

px para dp:

displayMetrics = context.getResources().getDisplayMetrics();
return (int) ((px/displayMetrics.density)+0.5);

27
@Vame A adição de 0,5 é usada para arredondar para CIMA até o valor inteiro mais próximo. A 0,5 é adicionada e o resultado do cálculo é convertido como int, fazendo com que trunque a mantissa e deixe a característica como um valor inteiro adequadamente arredondado.
— 22612 Kelly Copley

3
Isso nem sempre é correto. Quando tenho dois layouts, um dentro do outro, arredondo os cantos de cada visualização (um usando dp, outro convertendo em dp).
— Marek

Não tive problemas com essa técnica. Usei isso para diferentes layouts e sempre funcionou como esperado. Claro que usei isso alguns anos atrás e não tenho certeza se ainda funciona. Talvez você possa tentar a outra técnica na resposta do PanaVTEC. Também é possível que exista mais nos cantos arredondados do que apenas cálculos de dp / px.
— Bram

5
É a mesma solução adotada no site do desenvolvedor do Android, então acho que está correta :). http://developer.android.com/guide/practices/screens_support.html#dips-pels
— alocaly

1
+1 Obrigado cara. Trabalhou como um encanto! Obrigado por compartilhar esta solução conosco.
— Simon Dorociak

34

px para dp:

int valueInpx = ...;
int valueInDp= (int) TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP, valueInpx , getResources()
                .getDisplayMetrics());

7
Isto é de DP para PX, mas com esta correção: typedValue.applyDimension( TypedValue.COMPLEX_UNIT_PX, valueInDp , getResources() .getDisplayMetrics());é de PX para DP, também esta é a melhor resposta
— PaNaVTEC

1
@PaNaVTEC, a solução que você referenciou para dp para px está incorreta. applyDimensionproduz apenas pixels. Consulte android.googlesource.com/platform/frameworks/base/+/refs/heads/… em que nenhuma conversão é realizada COMPLEX_UNIT_PX.
— Stephen Niedzielski

Esta resposta está completamente errada. Retorna valores finais em pixels, nunca em dp. Se você olhar o código-fonte, para o caso TypedValue.COMPLEX_UNIT_DIP, value * metrics.densityserá retornado, onde você realmente precisa value * metrics.density. Então, o que você está getting` valueInDp` não está em dppor valueInPxem px.
— bitbybit

^ erro de digitação, eu quis dizer "... onde você realmente precisa value / metrics.density"
— bitbybit

31

Maneira eficiente de sempre

DP para Pixel:

private int dpToPx(int dp)
{
    return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
}

Pixel para DP:

private int pxToDp(int px)
{
    return (int) (px / Resources.getSystem().getDisplayMetrics().density);
}

Espero que isso ajude você.


4
Melhor porque não há necessidade de usar o Contexto :) Obrigado.
— Ayaz Alifov 13/03/19

2
Melhor resposta! Infelizmente, a resposta "Marcado como correto" está errada. Especialmente porque usa o displayMetrics.xdpique é diferente em qualquer dispositivo. Infelizmente, essa resposta errada tem mais votos positivos.
— 161618

legais. deve ser marcado como melhor resposta. kotlin:private fun dpToPx(dp: Int) = (dp * Resources.getSystem().displayMetrics.density).toInt()
— Raphael C

28

Basta ligar getResources().getDimensionPixelSize(R.dimen.your_dimension)para converter de dpunidades em pixels


3
De acordo com a documentação, é o mesmo que getDimension (), exceto que o valor retornado é convertido em pixels inteiros para uso como tamanho. Uma conversão de tamanho envolve arredondar o valor base e garantir que um valor base diferente de zero tenha pelo menos um pixel.
— CJBS 28/07

14

Use esta função

private int dp2px(int dp) {
    return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
}

6
px = dp * (dpi / 160)

dp = px * (160 / dpi)

embora, agora que penso nisso ... como haverá uma divisão por zero na fórmula original de Brams? O displayMetrics.densityDpi será 120, 160, 240 ou 320, nunca 0.
— ct_rob

2
(displayMetrics.densityDpi / 160)- esta parte pode obter 0 em dispositivos pequenos, onde calcula, por exemplo 120/160, ambos os valores são int, o que resulta em 0. O que acaba como (int) (px/0).

ah, você está certo. Então, tudo o que ele precisa fazer é usar um longo em sua fórmula: 160.0
— ct_rob 30/11

esta resposta é semelhante à superior porque o DisplayMetrics.DENSITY_DEFAULT tem 160 valor. mas, por favor, deixe-nos saber o que é o DPI aqui, como o calculamos.
— John smith

3

Na maioria dos casos, as funções de conversão são chamadas com frequência. Podemos otimizá-lo adicionando memorização. Portanto, ele não calcula sempre que a função é chamada.

Vamos declarar um HashMap que irá armazenar os valores calculados.

private static Map<Float, Float> pxCache = new HashMap<>();

Uma função que calcula valores de pixel:

public static float calculateDpToPixel(float dp, Context context) {

        Resources resources = context.getResources();
        DisplayMetrics metrics = resources.getDisplayMetrics();
        float px = dp * (metrics.densityDpi / 160f);
        return px;

    }

Uma função de memorização que retorna o valor do HashMap e mantém o registro dos valores anteriores.

A memorização pode ser implementada de diferentes maneiras em Java. Para Java 7 :

public static float convertDpToPixel(float dp, final Context context) {

        Float f = pxCache.get(dp);
        if (f == null) {
            synchronized (pxCache) {
                f = calculateDpToPixel(dp, context);
                pxCache.put(dp, f);
            }

        }

        return f;
    }

O Java 8 suporta a função Lambda :

public static float convertDpToPixel(float dp, final Context context) {

        pxCache.computeIfAbsent(dp, y ->calculateDpToPixel(dp,context));
}

Obrigado.


boa adição às soluções fornecidas.
— Bram

Eu ficaria surpreso se o cálculo da conversão não fosse pelo menos tão rápido quanto uma pesquisa no mapa, sem mencionar uma sincronizada. Você tem algum resultado de teste para demonstrar que essa otimização é benéfica? No Android, onde a memória de trabalho é limitada, a troca de memória pelo esforço de cálculo não é algo que você deve fazer sem um bom motivo.
— Lorne Laliberte


2

As funções abaixo funcionaram bem para mim em todos os dispositivos.

É retirado de https://gist.github.com/laaptu/7867851

public static float convertPixelsToDp(float px){
    DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
    float dp = px / (metrics.densityDpi / 160f);
    return Math.round(dp);
}

public static float convertDpToPixel(float dp){
    DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
    float px = dp * (metrics.densityDpi / 160f);
    return Math.round(px);
}

1

Você pode usar [DisplayMatrics][1]e determinar a densidade da tela. Algo assim:

int pixelsValue = 5; // margin in pixels
float d = context.getResources().getDisplayMetrics().density;
int margin = (int)(pixelsValue * d);

Pelo que me lembro, é melhor usar pisos para compensações e arredondamentos para larguras.



1

// para obter em termos de decimal / flutuante

public static float convertPixelsToDp(float px, Context context) {

    Resources resources = context.getResources();
    DisplayMetrics metrics = resources.getDisplayMetrics();
    float dp = px / (metrics.densityDpi / 160f);
    return Math.round(dp);
}



public static float convertDpToPixel(float dp, Context context) {
    DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
    float px = dp * (metrics.densityDpi / 160f);
    return Math.round(px);
}


// for  getting in terms of Integer

private int convertPxToDp(int px, Context context) {
    Resources resources = context.getResources();
    return Math.round(px / (resources.getDisplayMetrics().xdpi / DisplayMetrics.DENSITY_DEFAULT));
}


private int convertDpToPx(int dp, Context context) {
    Resources resources = context.getResources();

    return Math.round(dp * (resources.getDisplayMetrics().xdpi / DisplayMetrics.DENSITY_DEFAULT));

}

________________________________________________________________________________

public static float convertPixelsToDp(float px){
    DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
    float dp = px / (metrics.densityDpi / 160f);
    return Math.round(dp);
}

public static float convertDpToPixel(float dp){
    DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
    float px = dp * (metrics.densityDpi / 160f);
    return Math.round(px);
}


private int convertDpToPx(int dp){
    return Math.round(dp*(getResources().getDisplayMetrics().xdpi/DisplayMetrics.DENSITY_DEFAULT));

}

private int convertPxToDp(int px){
    return Math.round(px/(Resources.getSystem().getDisplayMetrics().xdpi/DisplayMetrics.DENSITY_DEFAULT));
}

1

Use estas extensões Kotlin:

/**
 * Converts Pixel to DP.
 */
val Int.pxToDp: Int
    get() = (this / Resources.getSystem().displayMetrics.density).toInt()

/**
 * Converts DP to Pixel.
 */
val Int.dpToPx: Int
    get() = (this * Resources.getSystem().displayMetrics.density).toInt()

0

Sinta-se livre para usar este método que escrevi:

int dpToPx(int dp)
{
    return (int) (dp * getResources().getDisplayMetrics().density + 0.5f);
}

0

A resposta aceita acima não é totalmente precisa. De acordo com as informações obtidas pela inspeção do código fonte do Android:

Resources.getDimension()e getDimensionPixelOffset()/ getDimensionPixelSize()diferem apenas no fato de o primeiro retornar, floatenquanto os dois últimos retornam o mesmo valor arredondado para o valor intapropriado. Para todos eles, o valor de retorno está em pixels brutos.

Todas as três funções são implementedy ligando Resources.getValue()e convertendo assim obtido TypedValueligando TypedValue.complexToDimension(), TypedValue.complexToDimensionPixelOffset()e TypedValue.complexToDimensionPixelSize(), respectivamente.

Portanto, se você deseja obter o valor "bruto" junto com a unidade especificada na fonte XML, chame Resources.getValue()e use os métodos da TypedValueclasse.


0

com ajuda de outras respostas, escrevi esta função.

public static int convertToPixels(Context context, int nDP)
{
        final float conversionScale = context.getResources().getDisplayMetrics().density; 
        return (int) ((nDP * conversionScale) + 0.5f) ;
}

0

Aqui está outra maneira de fazer isso usando extensões kotlin:

val Int.dpToPx: Int
    get() = Math.round(this * Resources.getSystem().displayMetrics.density)

val Int.pxToDp: Int
    get() = Math.round(this / Resources.getSystem().displayMetrics.density)

e então pode ser usado assim em qualquer lugar

12.dpToPx

244.pxToDp

0
DisplayMetrics displayMetrics = contaxt.getResources()
            .getDisplayMetrics();

    int densityDpi = (int) (displayMetrics.density * 160f);
    int ratio = (densityDpi / DisplayMetrics.DENSITY_DEFAULT);
    int px;
    if (ratio == 0) {
        px = dp;
    } else {
        px = Math.round(dp * ratio);

    }

0

a variação na resposta ct_robs acima, se você estiver usando números inteiros, que não apenas evita a divisão por 0, como também produz um resultado utilizável em dispositivos pequenos:

nos cálculos inteiros envolvendo divisão para maior precisão, multiplique primeiro antes de dividir para reduzir os efeitos de truncamento.

px = dp * dpi / 160
dp = px * 160 / dpi

5 * 120 = 600 / 160 = 3

ao invés de

5 * (120 / 160 = 0) = 0

se você quiser resultado arredondado, faça isso

px = (10 * dp * dpi / 160 + 5) / 10
dp = (10 * px * 160 / dpi + 5) / 10

10 * 5 * 120 = 6000 / 160 = 37 + 5 = 42 / 10 = 4
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.