Formel px zu dp, dp zu px android


150

Ich versuche, eine variable Anzahl von Pixeln zu dichteunabhängigen Pixeln zu berechnen und umgekehrt.

Diese Formel (px to dp): dp = (int)(px / (displayMetrics.densityDpi / 160));funktioniert bei kleinen Geräten nicht, da sie durch Null geteilt wird.

Dies ist meine dp to px Formel:

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

Könnte mir jemand ein paar Hinweise geben?


1
Konvertieren von dp-Einheiten in Pixel-Einheiten developer.android.com/guide/practices/…
Padma Kumar

@Bram: Ich denke deine Formel ist in Ordnung. Wie erhalten Sie eine Division durch Null? displayMetrics.densityDpi wird entweder 120, 160, 240 oder 320 sein, niemals 0.
ct_rob

Ich stimme @ct_rob zu. Der Mindestwert für displayMetrics.densityDpi / 160 beträgt 0,75. Sie müssen an der falschen Stelle auf int gecastet haben.
TomTaila

Antworten:


318

Hinweis: Die oben weit verbreitete Lösung basiert auf displayMetrics.density. In den Dokumenten wird jedoch erläutert, dass es sich bei diesem Wert um einen gerundeten Wert handelt, der mit den Bildschirm-Buckets verwendet wird. Z.B. Auf meinem Nexus 10 wird 2 zurückgegeben, wobei der tatsächliche Wert 298 dpi (real) / 160 dpi (Standard) = 1,8625 beträgt.

Abhängig von Ihren Anforderungen benötigen Sie möglicherweise die genaue Transformation, die folgendermaßen erreicht werden kann:

[Bearbeiten] Dies ist nicht dazu gedacht, mit der internen dp-Einheit von Android gemischt zu werden, da dies natürlich immer noch auf den Bildschirm-Buckets basiert. Verwenden Sie diese Option, wenn Sie eine Einheit wünschen, die auf verschiedenen Geräten dieselbe tatsächliche Größe wiedergeben soll.

Konvertiere dp in Pixel:

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

Pixel in dp konvertieren:

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

Beachten Sie, dass es xdpi- und ydpi-Eigenschaften gibt, die Sie vielleicht unterscheiden möchten, aber ich kann mir keine vernünftige Anzeige vorstellen, bei der sich diese Werte stark unterscheiden.


8
Dies berechnet auf vielen Geräten (einschließlich Ihres Nexus 10) nicht den korrekten Wert für dp / px! Wie Sie sagen, wird displayMetrics.density auf den nächsten Bildschirmbereich gerundet, aber auch die dp-Einheit! Versuchen Sie, ein Objekt mit einer Breite von 160 dp zu zeichnen, und direkt darunter zeichnen Sie ein anderes Objekt mit einer Breite von dpToPx (160) Pixel. Sie werden feststellen, dass die Größe der beiden Objekte unterschiedlich ist. Auch einige Telefone (wie Galaxy Mini und Galaxy S3 Mini) melden völlig falsche Werte für xdpi / ydpi, sodass Ihre Methoden auf diesen Handys völlig falsche Ergebnisse liefern.
Nibarius

@nibarius Ja, Sie können die Boxed-DP-Berechnungen von Android nicht mit den oben genannten kombinieren. Die obige Lösung ist als separater dichteunabhängiger Wert gedacht, der auf der genauen Gerätephysik basiert. Wird beispielsweise benötigt, wenn Sie auf verschiedenen Geräten eine Linie mit genau derselben tatsächlichen Länge anzeigen möchten. Wenn die xdpi / ydpi-Eingänge von einigen Geräten nicht richtig eingestellt werden, funktioniert dies dort natürlich nicht.
Bachi

1
xdpi und ydpi sollten nicht verwendet werden, da sie auf vielen Geräten ungenau sind, manchmal sehr oft. Nur DisplayMetrics.densityDpi ist zuverlässig, was bedauerlich ist, da es vom Design her ungenau ist. Weitere Informationen finden Sie im Google- Forenthread
Mark McClelland

1
Ich habe diese Antwort gefunden und tatsächlich viele ähnliche Lösungen verwendet, aber ich habe gerade die Dokumente durchgesehen und festgestellt, dass getDimensionPixelOffSet mit einer angegebenen Dimension (deklariert in dip / dp) den Versatz in Pixel genau wie der handgemachte Code zurückgibt. Ich habe einwandfrei getestet und gearbeitet. Ich hoffe es hilft!
William Gouvea

1
Mehhh, das gibt keinen korrekten Wert. Versuchen Sie: Resources r = getResources (); float px = TypedValue.applyDimension (TypedValue.COMPLEX_UNIT_DIP, 14, r.getDisplayMetrics ()); Wie hier beschrieben: stackoverflow.com/questions/4605527/converting-pixels-to-dp
Rik van Velzen

103

Ich habe mein Problem mit den folgenden Formeln gelöst. Mögen andere Menschen davon profitieren.

dp bis px:

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

px bis dp:

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

27
@Vame Die Addition von 0,5 wird verwendet, um auf den nächsten ganzzahligen Wert aufzurunden. Die 0,5 wird addiert und das Ergebnis der Berechnung wird als int umgewandelt, wodurch die Mantisse abgeschnitten wird und die Eigenschaft als richtig gerundeter ganzzahliger Wert verbleibt.
Kelly Copley

3
Das ist nicht immer richtig. Wenn ich zwei Layouts ineinander habe und dann die Ecken jeder Ansicht abrunde (eines mit dp, das andere mit dp konvertieren), stimmen die Ecken nicht überein!
Marek

Ich hatte keine Probleme mit dieser Technik. Ich habe dies für verschiedene Layouts verwendet und es hat immer wie erwartet funktioniert. Natürlich habe ich das vor ein paar Jahren benutzt und bin mir nicht sicher, ob es noch funktioniert. Vielleicht könnten Sie die andere Technik in der Antwort von PanaVTEC ausprobieren. Es könnte auch sein, dass das Abrunden von Ecken mehr beinhaltet als nur dp / px-Berechnungen.
Bram

5
Es ist die gleiche Lösung, die auf der Android-Entwicklerseite verwendet wird, also denke ich, dass es richtig ist :). http://developer.android.com/guide/practices/screens_support.html#dips-pels
alocaly

1
+1 Danke Mann. Lief wie am Schnürchen! Vielen Dank, dass Sie diese Lösung mit uns geteilt haben.
Simon Dorociak

34

px bis dp:

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

7
Dies ist von DP zu PX, aber mit dieser Korrektur: typedValue.applyDimension( TypedValue.COMPLEX_UNIT_PX, valueInDp , getResources() .getDisplayMetrics());ist von PX zu DP, auch dies ist die beste Antwort
PaNaVTEC

1
@PaNaVTEC, die Lösung, auf die Sie für dp to px verwiesen haben, ist falsch. applyDimensionliefert nur Pixel. Siehe android.googlesource.com/platform/frameworks/base/+/refs/heads/…, für die keine Konvertierung durchgeführt wird COMPLEX_UNIT_PX.
Stephen Niedzielski

Diese Antwort ist einfach falsch. Es gibt Endwerte in Pixel zurück, niemals in dp. Wenn Sie sich den Quellcode schauen, für den Fall TypedValue.COMPLEX_UNIT_DIP, value * metrics.densityzurückgegeben wird , wo Sie tatsächlich benötigen value * metrics.density. Also , was Sie getting` valueInDp` ist NICHT in dpfür valueInPxin px.
Bitbybit

^ Tippfehler, ich meinte "... wo Sie tatsächlich brauchen value / metrics.density"
Bitbybit

31

Effizienter Weg überhaupt

DP zu Pixel:

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

Pixel zu DP:

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

Hoffe das wird dir helfen.


4
Besser, weil es nicht nötig ist, Context zu verwenden :) Danke.
Ayaz Alifov

2
Beste Antwort! Leider ist die Antwort "Als richtig markiert" falsch. Zumal es verwendet, displayMetrics.xdpiwas auf jedem Gerät anders ist. Leider hat diese falsche Antwort zusätzlich die meisten positiven Stimmen.
Toom

nett. sollte als beste Antwort markiert werden. Kotlin:private fun dpToPx(dp: Int) = (dp * Resources.getSystem().displayMetrics.density).toInt()
Raphael C

28

Rufen Sie einfach getResources().getDimensionPixelSize(R.dimen.your_dimension)an, um von dpEinheiten in Pixel zu konvertieren


3
Gemäß der Dokumentation ist dies dasselbe wie getDimension (), außer dass der zurückgegebene Wert zur Verwendung als Größe in ganzzahlige Pixel konvertiert wird. Bei einer Größenkonvertierung wird der Basiswert gerundet und sichergestellt, dass ein Basiswert ungleich Null mindestens ein Pixel groß ist.
CJBS

14

Verwenden Sie diese Funktion

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)

obwohl, jetzt wo ich darüber nachdenke ... wie wird es jemals eine Division durch Null in Brams Originalformel geben? displayMetrics.densityDpi wird entweder 120, 160, 240 oder 320 sein, niemals 0.
ct_rob

2
(displayMetrics.densityDpi / 160)- Dieser Teil kann auf kleinen Geräten 0 bekommen, dort wird beispielsweise berechnet 120/160, dass beide Werte int sind, was zu 0 führt. Was als endet (int) (px/0).

Ah, du hast recht. Alles was er tun muss, ist ein Long in seiner Formel zu verwenden: 160.0
ct_rob

Diese Antwort ähnelt der oberen, da DisplayMetrics.DENSITY_DEFAULT den Wert 160 hat. Aber können Sie uns bitte mitteilen, was DPI hier ist, wie wir es berechnen?
John Smith

3

In den meisten Fällen werden Konvertierungsfunktionen häufig aufgerufen. Wir können es optimieren, indem wir Memoisierung hinzufügen. Es wird also nicht jedes Mal berechnet, wenn die Funktion aufgerufen wird.

Deklarieren wir eine HashMap, in der die berechneten Werte gespeichert werden.

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

Eine Funktion, die Pixelwerte berechnet:

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

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

    }

Eine Memo-Funktion, die den Wert von HashMap zurückgibt und die Aufzeichnung vorheriger Werte verwaltet.

Memoization kann in Java auf verschiedene Arten implementiert werden. Für 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 unterstützt die Lambda-Funktion :

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

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

Vielen Dank.


schöne Ergänzung zu den gegebenen Lösungen.
Bram

Es würde mich wundern, wenn die Conversion-Berechnung nicht mindestens so schnell wäre wie eine Kartensuche, ganz zu schweigen von einer synchronisierten. Haben Sie Testergebnisse, die belegen, dass diese Optimierung von Vorteil ist? Unter Android, wo der Arbeitsspeicher begrenzt ist, sollten Sie den Speicher gegen Rechenaufwand nicht ohne Grund handeln.
Lorne Laliberte


2

Die folgenden Funktionen haben für mich geräteübergreifend gut funktioniert.

Es stammt von 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

Sie können [DisplayMatrics][1]die Bildschirmdichte verwenden und bestimmen. Etwas wie das:

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

Wie ich mich erinnere, ist es besser, Bodenbeläge für Versätze und Rundungen für Breiten zu verwenden.



1

// um in Dezimal / Float zu kommen

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

Verwenden Sie diese Kotlin-Erweiterungen:

/**
 * 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

Fühlen Sie sich frei, diese Methode zu verwenden, die ich geschrieben habe:

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

0

Die oben akzeptierte Antwort ist nicht vollständig korrekt. Nach Informationen, die durch Überprüfen des Android-Quellcodes erhalten wurden:

Resources.getDimension()und getDimensionPixelOffset()/ getDimensionPixelSize()unterscheiden sich nur darin, dass die ersteren zurückgeben, floatwährend die beiden letzteren den gleichen Wert zurückgeben, der auf intangemessen gerundet ist . Für alle ist der Rückgabewert in Rohpixeln angegeben.

Alle drei Funktionen werden durch Aufruf implementiert Resources.getValue() und die Umwandlung so erhaltenen TypedValuedurch den Aufruf TypedValue.complexToDimension(), TypedValue.complexToDimensionPixelOffset()und TypedValue.complexToDimensionPixelSize(), respectively.

Wenn Sie daher einen "Rohwert" zusammen mit der in der XML-Quelle angegebenen Einheit erhalten möchten, rufen Sie Resources.getValue()die Methoden der TypedValueKlasse auf und verwenden Sie sie .


0

Mit Hilfe anderer Antworten habe ich diese Funktion geschrieben.

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

0

Hier ist eine andere Möglichkeit, dies mit Kotlin-Erweiterungen zu tun:

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

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

und dann kann es von überall so verwendet werden

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

Variation der obigen Antwort von ct_robs: Wenn Sie Ganzzahlen verwenden, wird nicht nur die Division durch 0 vermieden, sondern auch auf kleinen Geräten ein brauchbares Ergebnis erzielt:

Bei Ganzzahlberechnungen mit Division für höchste Präzision multiplizieren Sie zuerst, bevor Sie dividieren, um Kürzungseffekte zu reduzieren.

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

5 * 120 = 600 / 160 = 3

anstatt

5 * (120 / 160 = 0) = 0

Wenn Sie ein abgerundetes Ergebnis wünschen, tun Sie dies

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

10 * 5 * 120 = 6000 / 160 = 37 + 5 = 42 / 10 = 4
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.