Формула px до dp, dp до px android


150

Я намагаюся обчислити змінну кількість пікселів до щільних незалежних пікселів і навпаки.

Ця формула (px to dp): dp = (int)(px / (displayMetrics.densityDpi / 160));не працює на малих пристроях, оскільки ділиться на нуль.

Це моя формула dp to px:

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

Може хтось дасть мені вказівки?


1
Перетворення dp-одиниць у піксельні одиниці developer.android.com/guide/practices/…
Padma Kumar

@Bram: Я думаю, що ваша формула просто чудова. Як ви отримаєте ділення на нуль? displayMetrics.densityDpi буде або 120, 160, 240 або 320, ніколи 0.
ct_rob

Я згоден з @ct_rob. мінімальне значення displayMetrics.densityDpi / 160 буде 0,75. Напевно, ви брали участь у невідкладному місці.
TomTaila

Відповіді:


318

Примітка. Наведене вище широко використовуване рішення засноване на displayMetrics.density. Однак, документи пояснюють, що це значення - це округлене значення, яке використовується із екранами "відра". Напр. на моєму Nexus 10 він повертає 2, де реальне значення буде 298dpi (реальне) / 160dpi (за замовчуванням) = 1,88625.

Залежно від ваших вимог, вам може знадобитися точне перетворення, яке можна досягти так:

[Редагувати] Це не призначено для змішування з внутрішнім dp-блоком Android, оскільки це, звичайно, все ще базується на екранах. Використовуйте це там, де ви хочете, щоб пристрій, який повинен мати однаковий реальний розмір на різних пристроях.

Перетворити dp в піксель:

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

Перетворити піксель у dp:

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

Зауважте, що є властивості xdpi та ydpi, ви можете розрізнити, але я не можу уявити здоровий дисплей, де ці значення сильно відрізняються.


8
Це не обчислює правильне значення для dp / px на багатьох пристроях (включаючи ваш Nexus 10)! Як ви кажете, displayMetrics.density округлюється до найближчого відрізка екрана, але так само dp-одиниця! Спробуйте намалювати один об'єкт шириною 160dp і трохи нижче нього ви намалюєте інший об’єкт, що має dpToPx (160) пікселів, і ви побачите, що розміри двох об'єктів різні. Також деякі телефони (наприклад, Galaxy Mini і Galaxy S3 Mini) повідомляють про абсолютно неправильні значення для xdpi / ydpi, тому на цих телефонах ваші методи повернуть абсолютно неправильні результати.
nibarius

@nibarius Так, ви не можете поєднувати обчислені в коробці dp обчислення Android з наведеними вище. Вищевказане рішення розуміється як окреме незалежне значення щільності, засноване на точній фізиці пристрою. Потрібно, наприклад, там, де ви хочете показати лінію точно однакової реальної довжини на різних пристроях. Звичайно, якщо входи xdpi / ydpi деякими пристроями не встановлені правильно, вони там не працюватимуть.
Бачі

1
xdpi та ydpi не слід використовувати, оскільки вони неточні на багатьох пристроях, іноді - на багатьох. Надійний лише DisplayMetrics.densityDpi, що шкода, оскільки дизайн неточний. Докладнішу інформацію див. У темі форуму Google: groups.google.com/forum/#!topic/android-developers/g56jV0Hora0
Марк Макклелленд

1
Я знайшов цю відповідь і фактично використав безліч подібних рішень, але я просто пройшов документи і знайшов getDimensionPixelOffSet з заданим виміром (оголошеним у dip / dp) повертає зміщення в пікселях так само, як код ручної роботи. Я тестував і працював бездоганно. Сподіваюся, це допомагає!
william gouvea

1
Мехх, це не дає правильного значення. Спробуйте: Resources r = getResources (); float px = TypedValue.applyDimension (TypedValue.COMPLEX_UNIT_DIP, 14, r.getDisplayMetrics ()); Як описано тут: stackoverflow.com/questions/4605527/converting-pixels-to-dp
Rik van Velzen

103

Я вирішив свою проблему за допомогою наступних формул. Нехай інші люди отримують від цього користь.

dp до px:

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

px до dp:

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

27
@Vame Додавання 0,5 використовується для округлення до найближчого цілого значення .. 0,5 додається, а потім результат обчислення подається у вигляді int, що призводить до обрізання мантіси та залишаючи характеристику як правильно округлене ціле число.
Келлі Коплі

3
Це не завжди правильно. Коли у мене два макети один всередині іншого, а потім я закруглюю кути кожного виду (один за допомогою dp, інший перетворюється в dp) кути не відповідають!
Марек

У мене не було проблем з цією технікою. Я використовував це для різних макетів, і це завжди працювало так, як очікувалося. Звичайно, я використав це кілька років тому, і я не впевнений, чи все ще працює. Можливо, ви могли б спробувати іншу техніку у відповіді PanaVTEC. Можливо також, що для кутів округлення є більше, ніж просто обчислення dp / px.
Брем

5
Це те саме рішення, яке прийнято на сайті розробників Android, так що, мабуть, це правильно :). http://developer.android.com/guide/practices/screens_support.html#dips-pels
alocaly

1
+1 Спасибі людині. Працював як шарм! Дякуємо, що поділилися цим рішенням з нами.
Саймон Дорочак

34

px до dp:

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

7
Це від DP до PX, але з цією корекцією: typedValue.applyDimension( TypedValue.COMPLEX_UNIT_PX, valueInDp , getResources() .getDisplayMetrics());це від PX до DP, також це найкраща відповідь
PaNaVTEC

1
@PaNaVTEC, рішення, на яке ви посилалися для dp до px, неправильне. applyDimensionдає лише пікселі. Див. Android.googlesource.com/platform/frameworks/base/+/refs/heads/…, де не проводиться перетворення COMPLEX_UNIT_PX.
Stephen Niedzielski

Ця відповідь явно неправильна. Він повертає кінцеві значення в пікселях, ніколи в dp. Якщо ви подивіться на вихідний код, за винятком випадку TypedValue.COMPLEX_UNIT_DIP, value * metrics.densityповертається, коли ви на самому справі потрібно value * metrics.density. Так що ви getting` valueInDp` НЕ в dpпротягом valueInPxв px.
бітбіт

^ друкарня, я мав на увазі «... де вам насправді потрібно value / metrics.density»
бітбібіт

31

Ефективний спосіб

DP до Pixel:

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

Піксель до DP:

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

Сподіваюся, що це вам допоможе.


4
Краще, тому що не потрібно використовувати Context :) Дякую.
Аяз Аліфов

2
Найкраща відповідь! На жаль, відповідь "Позначено як правильну" неправильна. Тим більше, що він використовує те, displayMetrics.xdpiщо відрізняється на будь-якому пристрої. На жаль, ця неправильна відповідь додатково має найбільше відгуків.
1818

приємно. повинні бути позначені як найкраща відповідь. kotlin:private fun dpToPx(dp: Int) = (dp * Resources.getSystem().displayMetrics.density).toInt()
Рафаель C

28

Просто зателефонуйте getResources().getDimensionPixelSize(R.dimen.your_dimension)для перетворення з dpодиниць у пікселі


3
Згідно з документацією, це те саме, що і getDimension (), за винятком того, що повернене значення перетворюється на цілі пікселі для використання як розмір. Перетворення розміру включає округлення базового значення та забезпечення того, що ненульове базове значення має розмір принаймні одного пікселя.
CJBS


6
px = dp * (dpi / 160)

dp = px * (160 / dpi)

хоча, коли я думаю про це ... як коли-небудь буде ворожіння по нулю в оригінальній формулі Брамса? displayMetrics.densityDpi буде або 120, 160, 240 або 320, ніколи 0.
ct_rob

2
(displayMetrics.densityDpi / 160)- ця частина може отримати 0 на невеликих пристроях, там вона обчислює, наприклад 120/160, обидва значення є int, що призводить до 0. Що закінчується як (int) (px/0).

ах, ти маєш рацію. Тому все, що йому потрібно зробити, - це використовувати довгий у своїй формулі: 160.0
ct_rob

ця відповідь схожа на верхню, тому що DisplayMetrics.DENSITY_DEFAULT має 160 значень. але чи можете ви, будь ласка, повідомити нам, що таке DPI, як ми його обчислимо.
Джон Сміт

3

У більшості випадків функції перетворення викликаються часто. Ми можемо оптимізувати його, додавши запам'ятовування. Отже, він не обчислює кожен раз, коли функція викликається.

Давайте оголосимо HashMap, який буде зберігати обчислені значення.

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

Функція, яка обчислює значення пікселів:

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 і підтримує запис попередніх значень.

Пам'ять може бути реалізована різними способами на Java. Для 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 підтримує функцію Lambda :

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

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

Дякую.


приємне доповнення до поданих рішень.
Брем

Я був би здивований, якби розрахунок конверсії був не принаймні таким швидким, як пошук карти, не кажучи вже про синхронізований. Чи є у вас якісь результати тестів, щоб продемонструвати, що ця оптимізація вигідна? На Android, де робоча пам'ять обмежена, торгова пам'ять для розрахункових зусиль - це не те, що слід робити без поважних причин.
Lorne Laliberte


2

Нижче функції функціонували для мене на різних пристроях.

Воно взято з 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

Ви можете використовувати [DisplayMatrics][1]та визначати щільність екрана. Щось на зразок цього:

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

Як я пам’ятаю, краще використовувати підлогу для зміщення, а округлення для ширини.



1

// для отримання в перерахунку на десяткові числа / Float

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

Скористайтеся цими розширеннями 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

Не соромтеся використовувати цей метод, я написав:

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

0

Відповідь, прийнята вище, не є повністю точною. Відповідно до інформації, отриманої при перевірці вихідного коду Android:

Resources.getDimension()та getDimensionPixelOffset()/ getDimensionPixelSize()відрізняються лише тим, що перший повертає, floatтоді як останні два повертають те саме значення, округлене до intвідповідного. Для всіх них повернене значення знаходиться в необроблених пікселях.

Усі три функції реалізуються за допомогою виклику Resources.getValue()та перетворення, отриманих таким чином TypedValueпри виклику TypedValue.complexToDimension(), TypedValue.complexToDimensionPixelOffset()і TypedValue.complexToDimensionPixelSize(), відповідно.

Тому, якщо ви хочете отримати "необроблене" значення разом з одиницею, зазначеною у джерелі XML, зателефонуйте Resources.getValue()та використовуйте методи TypedValueкласу.


0

за допомогою інших відповідей я написав цю функцію.

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

0

Ось інший спосіб зробити це за допомогою розширень 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)

і тоді його можна використовувати так з будь-якого місця

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

варіація на ct_robs відповідь вище, якщо ви використовуєте цілі числа, що не лише дозволяє уникнути ділення на 0, але також створює корисний результат на невеликих пристроях:

в цілих обчисленнях, що включають ділення для найбільшої точності, помножте спочатку перед діленням, щоб зменшити ефекти усічення.

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

5 * 120 = 600 / 160 = 3

замість

5 * (120 / 160 = 0) = 0

якщо ви хочете округлий результат, зробіть це

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

10 * 5 * 120 = 6000 / 160 = 37 + 5 = 42 / 10 = 4
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.