Pregunta

Estoy tratando de calcular una cantidad variable de píxeles a píxeles y viceversa de densidad.

Esta fórmula (px to dp): dp = (int)(px / (displayMetrics.densityDpi / 160)); no funciona en dispositivos pequeños porque está dividido por cero.

Esta es mi fórmula DP a PX:

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

¿Alguien podría darme algunos consejos?

¿Fue útil?

Solución

Nota: La solución ampliamente utilizada anterior se basa en displayMetrics.density. Sin embargo, los documentos explican que este valor es un valor redondeado, utilizado con la pantalla 'cubos'. P.ej. En mi nexus 10 devuelve 2, donde el valor real sería 298DPI (real) / 160DPI (predeterminado) = 1.8625.

Dependiendo de sus requisitos, es posible que necesite la transformación exacta, que se puede lograr así:

Editar] Esto no está destinado a mezclarse con la unidad DP interna de Android, ya que, por supuesto, todavía se basa en los cubos de pantalla. Use esto donde desee una unidad que deba rendir el mismo tamaño real en diferentes dispositivos.

Convertir DP a píxel:

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

Convertir Pixel a DP:

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

Tenga en cuenta que hay propiedades XDPI y YDPI, es posible que desee distinguir, pero no puedo imaginar una pantalla sensata donde estos valores difieran enormemente.

Otros consejos

Resolví mi problema usando las siguientes fórmulas. Que otras personas se beneficien de ello.

DP a PX:

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

PX a DP:

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

PX a DP:

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

Solo llama getResources().getDimensionPixelSize(R.dimen.your_dimension) para convertir de dp unidades a píxeles

Manera eficiente

DP a Pixel:

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

Pixel a DP:

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

Espero que esto te ayudará.

Usa esta función

private int dp2px(int dp) {
    return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
}
px = dp * (dpi / 160)

dp = px * (160 / dpi)

En la mayoría de los casos, las funciones de conversión se llaman con frecuencia. Podemos optimizarlo agregando memoización. Por lo tanto, no calcula cada vez que se llama la función.

Declaremos un hashmap que almacenará los valores calculados.

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

Una función que calcula los valores de píxeles:

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

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

    }

Una función de memorización que devuelve el valor de HashMap y mantiene el registro de valores anteriores.

La memoización se puede implementar de diferentes maneras en 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;
    }

Java 8 admite la función lambda :

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

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

Gracias.

A continuación, las funciones me funcionaron bien en todos los dispositivos.

Se toma 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);
}

Puedes usar [DisplayMatrics][1] y determinar la densidad de pantalla. Algo como esto:

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

Como recuerdo, es mejor usar pisos para compensaciones y redondeo para anchos.

Si está buscando una calculadora en línea para convertir DP, SP, pulgadas, milímetros, puntos o píxeles hacia y otro en diferentes densidades de pantalla, Esta es la herramienta más completa que conozco.

// para obtener en términos de decimal/flotante

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));
}

Siéntase libre de usar este método que escribí:

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

La respuesta aceptada anteriormente no es completamente precisa. De acuerdo con la información obtenida por inspeccionar el código fuente de Android:

Resources.getDimension() y getDimensionPixelOffset()/getDimensionPixelSize() difieren solo en que el anterior regresa float mientras que los dos últimos devuelven el mismo valor redondeado a int apropiado. Para todos ellos, el valor de retorno está en píxeles sin procesar.

Las tres funciones se implementan llamando Resources.getValue() y conversión así obtenida TypedValue llamando TypedValue.complexToDimension(), TypedValue.complexToDimensionPixelOffset() y TypedValue.complexToDimensionPixelSize(), respectivamente.

Por lo tanto, si desea obtener un valor "sin procesar" junto con la unidad especificada en la fuente XML, llame Resources.getValue() y utilizar métodos de la TypedValue clase.

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);

    }

Variación en la respuesta CT_ROBS arriba, si está utilizando enteros, que no solo evita la división por 0, sino que también produce un resultado utilizable en dispositivos pequeños:

En los cálculos enteros que involucran la división para la mayor precisión, se multiplica primero antes de dividir para reducir los efectos de truncamiento.

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

5 * 120 = 600 / 160 = 3

en vez de

5 * (120 / 160 = 0) = 0

Si quieres un resultado redondeado, haz esto

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

10 * 5 * 120 = 6000 / 160 = 37 + 5 = 42 / 10 = 4

Con la ayuda de otras respuestas, escribí esta función.

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

Aquí hay una otra forma de hacerlo usando extensiones de 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)

y luego se puede usar de esta parte desde cualquier lugar

12.dpToPx

244.pxToDp
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top