Formula piksel dp, dp piksel android için


150

Ben yoğunluk bağımsız piksel ve tersi için piksel değişken bir miktar hesaplamak için çalışıyorum.

Bu formül (px to dp): dp = (int)(px / (displayMetrics.densityDpi / 160));, sıfıra bölündüğü için küçük cihazlarda çalışmaz.

Bu benim dp piksel formülü:

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

Birisi bana bazı işaretçiler verebilir mi?


1
DP birimlerini piksel birimlerine dönüştürme developer.android.com/guide/practices/…
Padma Kumar

@Bram: Bence formülünüz gayet iyi. Nasıl sıfıra bölünürsünüz? displayMetrics.densityDpi 120, 160, 240 veya 320, asla 0 olacaktır.
ct_rob

@Ct_rob'a katılıyorum. displayMetrics.densityDpi / 160 minimum değeri 0,75 olacaktır. Yanlış yere int atmış olmalısınız.
TomTaila

Yanıtlar:


318

Not: Yukarıda yaygın olarak kullanılan çözüm esas alınmıştır displayMetrics.density. Ancak dokümanlar bu değerin, ekran 'kovaları' ile kullanılan yuvarlatılmış bir değer olduğunu açıklar. Örneğin. Nexus 10'umda gerçek değer 298dpi (gerçek) / 160dpi (varsayılan) = 1.8625 olacak şekilde 2 değerini döndürür.

Gereksinimlerinize bağlı olarak, aşağıdaki gibi gerçekleştirilebilecek kesin dönüşüme ihtiyacınız olabilir:

[Düzenle] Bu, elbette hala ekran kovalarına dayandığından, Android'in dahili dp birimi ile karıştırılması amaçlanmamıştır. Bunu, farklı cihazlarda aynı gerçek boyutu işlemesi gereken bir birimin olmasını istediğiniz yerde kullanın.

DP'yi piksele dönüştür:

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

Piksel dönüştürmek dp:

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

Xdpi ve ydpi özellikleri olduğunu unutmayın, ayırt etmek isteyebilirsiniz, ancak bu değerlerin büyük ölçüde farklı olduğu bir aklı başında ekran hayal edemiyorum.


8
Bu, birçok cihazda (Nexus 10'unuz dahil) dp / px için doğru değeri hesaplamaz! Dediğiniz gibi displayMetrics.density en yakın ekran kovasına yuvarlanır, ancak dp ünitesi de öyle! 160dp genişliğinde bir nesne çizmeyi deneyin ve hemen altında dpToPx (160) piksel genişliğinde başka bir nesne çizin ve iki nesnenin boyutunun farklı olduğunu göreceksiniz. Ayrıca bazı telefonlar (Galaxy Mini ve Galaxy S3 Mini gibi) xdpi / ydpi için tamamen yanlış değerler bildiriyorlar, bu nedenle bu telefonlarda yöntemleriniz tamamen yanlış sonuçlar verecektir.
nibarius

@nibarius Evet, Android'in kutulu dp hesaplamalarını yukarıdakilerle karıştıramazsınız. Yukarıdaki çözüm, tam cihaz fiziğine dayalı olarak ayrı bir yoğunluktan bağımsız değer olarak kastedilmektedir. Örneğin, farklı cihazlarda aynı gerçek uzunluğu bir çizgiyi göstermek istediğiniz yerde gereklidir. Elbette xdpi / ydpi girişleri bazı aygıtlar tarafından doğru şekilde ayarlanmamışsa, orada çalışmaz.
Bachi

1
xdpi ve ydpi kullanılmamalıdır, çünkü çoğu cihazda bazen çok fazla yanlıştırlar. Sadece DisplayMetrics.densityDpi güvenilirdir, talihsizdir, çünkü tasarım tarafından kesin değildir. Daha fazla bilgi için Google forum konusuna bakın: groups.google.com/forum/#!topic/android-developers/g56jV0Hora0
Mark McClelland

1
Bu yanıtı buldum ve aslında birçok benzer çözüm kullandım, ancak sadece dokümanlardan geçtim ve getDimensionPixelOffSet'in bir boyut verildi (dip / dp'de bildirildi) ile el yapımı kod gibi ofseti döndürdüğünü buldum. Test ettim ve kusursuz çalıştım. Umarım yardımcı olur!
william gouvea

1
Mehhh, bu doğru bir değer vermiyor. Deneyin: Kaynaklar r = getResources (); float px = TypedValue.applyDimension (TypedValue.COMPLEX_UNIT_DIP, 14, r.getDisplayMetrics ()); Burada açıklandığı gibi: stackoverflow.com/questions/4605527/converting-pixels-to-dp
Rik van Velzen

103

Sorunumu aşağıdaki formülleri kullanarak çözdüm. Diğer insanlar bundan faydalansın.

dp piksel için:

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

pikselden dp'ye:

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

27
@Vame 0,5 eklenmesi, en yakın tamsayı değerine YUKARI yuvarlamak için kullanılır. 0,5 eklenir ve sonra hesaplamanın sonucu, mantisin kesilmesine ve karakteristiğin düzgün yuvarlatılmış bir tamsayı değeri olarak ayrılmasına neden olacak bir int olarak verilir.
Kelly Copley

3
Bu her zaman doğru değildir. Birbiri içinde iki düzen varsa ve sonra her görünümün köşelerini (dp kullanarak, dp kullanarak diğer) yuvarlak yuvarlak köşeleri eşleşmiyor!
Marek

Bu teknikle ilgili herhangi bir sorun yaşamadım. Bunu farklı düzenler için kullandım ve her zaman beklendiği gibi çalıştı. Tabii bunu birkaç yıl önce kullandım ve hala işe yarayıp yaramadığından emin değilim. Belki de PanaVTEC'in cevabındaki diğer tekniği deneyebilirsiniz. Köşeleri yuvarlamak için sadece dp / px hesaplamalarından daha fazlası olabilir.
Bram

5
Android geliştirici sitesinde benimsenen aynı çözüm, bu yüzden sanırım doğru :). http://developer.android.com/guide/practices/screens_support.html#dips-pels
alocaly

1
+1 Teşekkürler dostum. Bir cazibe gibi çalıştı! Bu çözümü bizimle paylaştığınız için teşekkür ederiz.
Simon Dorociak

34

pikselden dp'ye:

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

7
Bu DP'den PX'e, ancak bu düzeltme ile: typedValue.applyDimension( TypedValue.COMPLEX_UNIT_PX, valueInDp , getResources() .getDisplayMetrics());
PX'den

1
@PaNaVTEC, dp to px için başvurduğunuz çözüm yanlış. applyDimensionyalnızca piksel verir. Bkz. Android.googlesource.com/platform/frameworks/base/+/refs/heads/… için dönüşüm gerçekleştirilmemiştir COMPLEX_UNIT_PX.
Stephen Niedzielski

Bu cevap gayet yanlış. Nihai değerleri piksel olarak döndürür, asla girmez dp. Kaynak koduna bakarsanız, durum için TypedValue.COMPLEX_UNIT_DIP, value * metrics.densitygerçekten ihtiyacınız olan yere döndürülür value * metrics.density. Yani ne valueInDp` getting` edilir OLMADIĞINDAN dpiçin valueInPxde px.
bitbybit

^ yazım hatası, demek istediğim "... aslında ihtiyacınız olan yer value / metrics.density"
bitbybit

31

Şimdiye kadar verimli yol

DP - Piksel arası:

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

Pikselden DP'ye:

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

Umarım bu size yardımcı olacaktır.


4
Daha iyi çünkü Context kullanmaya gerek yok :) Teşekkürler.
Ayaz Alifov

2
En iyi cevap! Maalesef "Doğru olarak işaretlendi" yanıtı yanlış. Özellikle displayMetrics.xdpiherhangi bir cihazda farklı olanı kullandığı için . Ne yazık ki, bu yanlış cevap ek olarak en fazla oyu var.
toom

Güzel. en iyi cevap olarak işaretlenmelidir. kotlin:private fun dpToPx(dp: Int) = (dp * Resources.getSystem().displayMetrics.density).toInt()
Raphael C

28

Birimlerden piksellere getResources().getDimensionPixelSize(R.dimen.your_dimension)dönüştürmek için arayındp


3
Belgelere göre, getDimension () ile aynıdır, ancak döndürülen değer boyut olarak kullanmak için tamsayı piksele dönüştürülür. Bir boyut dönüşümü, taban değerinin yuvarlanmasını ve sıfır olmayan bir taban değerinin en az bir piksel boyutunda olmasını sağlar.
CJBS

14

Bu işlevi kullan

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)

Yine de, şimdi düşünüyorum ki ... nasıl Brams orijinal formülünde sıfıra bölünme olacak? displayMetrics.densityDpi 120, 160, 240 veya 320, asla 0 olacaktır.
ct_rob

2
(displayMetrics.densityDpi / 160)- Bu bölüm küçük cihazlarda 0 alabilir, orada 120/160her iki değer de int olarak hesaplanır , bu da 0 ile sonuçlanır (int) (px/0).

ah, haklısın. Tek yapması gereken formülünde uzun kullanmak: 160.0
ct_rob

DisplayMetrics.DENSITY_DEFAULT değeri 160 olduğundan bu yanıt üstteki soruya benzer. ama burada DPI'ın ne olduğunu nasıl hesapladığımızı bize bildirir misiniz?
John smith

3

Çoğu durumda, dönüştürme işlevleri sık sık çağrılır. Not ekleyerek optimize edebiliriz. Bu nedenle, işlev her çağrıldığında hesaplanmaz.

Hesaplanan değerleri depolayacak bir HashMap beyan edelim.

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

Piksel değerlerini hesaplayan bir işlev:

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

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

    }

HashMap'ten değeri döndüren ve önceki değerlerin kaydını tutan bir not işlevi.

Notlama, Java'da farklı şekillerde uygulanabilir. Java 7 için :

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 Lambda işlevini destekler :

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

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

Teşekkürler.


verilen çözümlere güzel bir ek.
Bram

Dönüşüm hesaplamasının en azından bir harita araması kadar hızlı olmasaydı, senkronize edilmiş bir hesaptan bahsetmemeye şaşırdım. Bu optimizasyonun faydalı olduğunu gösteren herhangi bir test sonucunuz var mı? Çalışma belleğinin sınırlı olduğu Android'de, hesaplama çabası için işlem belleği iyi bir sebep olmadan yapmanız gereken bir şey değildir.
Lorne Laliberte


2

Aşağıdaki işlevler cihazlar arasında benim için iyi çalıştı.

Https://gist.github.com/laaptu/7867851 adresinden alınır.

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

[DisplayMatrics][1]Ekran yoğunluğunu kullanabilir ve belirleyebilirsiniz. Bunun gibi bir şey:

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

Hatırladığım gibi, ofsetler için döşeme ve genişlikler için yuvarlama kullanmak daha iyidir.



1

// Ondalık / Kayan Nokta açısından almak için

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

Şu Kotlin uzantılarını kullanın:

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

Yazdığım bu yöntemi kullanmaktan çekinmeyin:

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

0

Yukarıda kabul edilen cevap tam olarak doğru değildir. Android kaynak kodunu inceleyerek elde edilen bilgilere göre:

Resources.getDimension()ve getDimensionPixelOffset()/ getDimensionPixelSize()ancak sadece birincisi geri floatdönerken, ikincisi intuygun değere yuvarlanmış aynı değeri döndürür . Hepsi için, dönüş değeri ham piksel cinsindendir.

Tüm üç fonksiyon arayarak implementedy olan Resources.getValue()ve bu şekilde dönüştürerek elde TypedValuearayarak TypedValue.complexToDimension(), TypedValue.complexToDimensionPixelOffset()ve TypedValue.complexToDimensionPixelSize()sırasıyla.

Bu nedenle, XML kaynağında belirtilen birim ile birlikte "raw" değerini elde etmek istiyorsanız Resources.getValue(), TypedValuesınıfın yöntemlerini çağırın ve kullanın .


0

diğer cevapların yardımıyla bu fonksiyonu yazdım.

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

0

İşte kotlin uzantılarını kullanarak yapmanın başka bir yolu:

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

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

ve sonra her yerden böyle kullanılabilir

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

Yukarıdaki ct_robs cevabı varyasyonu, eğer tamsayı kullanıyorsanız, sadece 0'a bölünmeyi önlemekle kalmaz, aynı zamanda küçük cihazlarda da kullanılabilir bir sonuç verir:

tamsayı hesaplamalarında, en yüksek hassasiyet için bölmeyi içeren, kesme etkilerini azaltmak için bölmeden önce çarpın.

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

5 * 120 = 600 / 160 = 3

onun yerine

5 * (120 / 160 = 0) = 0

yuvarlak sonuç istiyorsanız bunu yapın

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

10 * 5 * 120 = 6000 / 160 = 37 + 5 = 42 / 10 = 4
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.