공식 px에서 dp로, dp에서 px로 안드로이드


150

밀도 독립 픽셀과 그 반대로 가변 픽셀 양을 계산하려고합니다.

이 수식 (px to dp): dp = (int)(px / (displayMetrics.densityDpi / 160));은 0으로 나뉘어져 있기 때문에 소형 장치에서는 작동하지 않습니다.

이것은 내 dp to px 공식입니다.

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

누군가 나에게 포인터를 줄 수 있습니까?


1
dp 단위를 픽셀 단위로 변환 developer.android.com/guide/practices/…
Padma Kumar

@ 브람 : 당신의 공식은 괜찮다고 생각합니다. 0으로 나누는 방법은 무엇입니까? displayMetrics.densityDpi는 120, 160, 240 또는 320이며 절대 0이 아닙니다.
ct_rob November

@ct_rob에 동의합니다. displayMetrics.densityDpi / 160 최소값은 0.75입니다. 잘못된 위치에 int로 캐스팅해야합니다.
TomTaila

답변:


318

참고 : 위의 널리 사용되는 솔루션은를 기반으로 displayMetrics.density합니다. 그러나 문서에서는이 값이 반올림 값이며 화면 '버킷'과 함께 사용된다고 설명합니다. 예 : 내 Nexus 10에서는 2를 반환하며 실제 값은 298dpi (실제) / 160dpi (기본값) = 1.8625입니다.

요구 사항에 따라 다음과 같이 달성 할 수있는 정확한 변환이 필요할 수 있습니다.

[편집] 이것은 여전히 ​​스크린 버킷을 기반으로하기 때문에 안드로이드의 내부 dp 유닛과 혼합되어서는 안됩니다. 다른 장치에서 동일한 실제 크기를 렌더링해야하는 장치를 원하는 경우이 옵션을 사용하십시오.

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
많은 기기 (Nexus 10 포함)에서 dp / px에 대한 올바른 값을 계산하지 않습니다! 당신이 말하는 것처럼 displayMetrics.density는 가장 가까운 화면 버킷으로 반올림되지만 dp 단위도 마찬가지입니다! 너비가 160dp 인 객체 하나를 그리면 바로 그 아래에 너비가 dpToPx (160) 픽셀 인 다른 객체가 그려지며 두 객체의 크기가 다르다는 것을 알 수 있습니다. 또한 일부 전화기 (예 : Galaxy Mini 및 Galaxy S3 Mini)는 xdpi / ydpi에 대해 완전히 잘못된 값을보고하므로 이러한 전화기에서는 메서드가 완전히 잘못된 결과를 반환합니다.
nibarius

@nibarius 그렇습니다. Android의 박스형 dp 계산을 위와 혼합 할 수 없습니다. 위의 솔루션은 정확한 장치 물리학을 기반으로 별도의 밀도 독립 값을 의미합니다. 예를 들어 다른 장치에서 정확히 동일한 실제 길이의 선을 표시하려는 경우 필요합니다. 물론 일부 장치에서 xdpi / ydpi 입력이 올바르게 설정되어 있지 않으면 작동하지 않습니다.
Bachi

1
xdpi와 ydpi는 많은 장치에서 가끔씩 정확하지 않기 때문에 사용해서는 안됩니다. DisplayMetrics.densityDpi 만 신뢰할 수 있으며 이는 설계 상 정확하지 않기 때문에 불행합니다. 자세한 내용은 Google 포럼 스레드를 참조하십시오 : groups.google.com/forum/#!topic/android-developers/g56jV0Hora0
Mark McClelland

1
이 응답을 발견하고 실제로 유사한 솔루션을 많이 사용했지만 방금 문서를 살펴보고 치수가 지정된 getDimensionPixelOffSet (dip / dp로 선언)이 수제 코드와 같이 오프셋을 픽셀 단위로 반환한다는 것을 알았습니다. 나는 완벽하게 테스트하고 일했다. 그것이 도움이되기를 바랍니다!
william gouvea

1
Mehhh, 이것은 올바른 가치를 제공하지 않습니다. 시도 : 자원 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로 변환) 모서리가 일치하지 않습니다!
Marek

이 기술에는 아무런 문제가 없었습니다. 나는 이것을 다른 레이아웃에 사용했으며 항상 예상대로 작동했습니다. 물론 몇 년 전에 사용했지만 여전히 작동하는지 확실하지 않습니다. 아마도 PanaVTEC의 답변에서 다른 기술을 시도해 볼 수 있습니다. 또한 dp / px 계산보다 반올림 코너가 더 많을 수도 있습니다.
Bram

5
그것은 안드로이드 개발자 사이트에서 채택 된 것과 동일한 솔루션이므로 올바른 것 같습니다 :). http://developer.android.com/guide/practices/screens_support.html#dips-pels
alocaly

1
+1 감사합니다. 매력처럼 일했다! 이 솔루션을 우리와 공유해 주셔서 감사합니다.
Simon Dorociak

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 to 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. 그래서 당신이 valueInDp`을 getting`하는 것은 아닙니다 dp위한 valueInPx에서 px.
비트 비트

^ 오타, 나는 의미 "당신이 실제로 필요로하는 곳에 ... value / metrics.density"
bitbybit을

31

효율적인 방법

DP에서 픽셀로 :

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
컨텍스트를 사용할 필요가 없기 때문에 더 좋습니다 :) 감사합니다.
Ayaz Alifov 2016 년

2
최고의 답변! 불행히도 "정답으로 표시됨"답변이 잘못되었습니다. 특히 displayMetrics.xdpi모든 장치에서 다른 것을 사용하기 때문에 . 안타깝게도이 잘못된 답변에는 추가 의견이 가장 많습니다.
toom

좋은. 최선의 답변으로 표시해야합니다. kotlin :private fun dpToPx(dp: Int) = (dp * Resources.getSystem().displayMetrics.density).toInt()
Raphael C

28

단위에서 픽셀 getResources().getDimensionPixelSize(R.dimen.your_dimension)로 변환하기 위해 전화하십시오.dp


3
문서에 따라 이것은 반환 값이 크기로 사용하기 위해 정수 픽셀로 변환된다는 점을 제외하면 getDimension ()과 동일합니다. 크기 변환에는 기본 값을 반올림하고 0이 아닌 기본 값의 크기가 적어도 하나의 픽셀이되도록합니다.
CJBS


6
px = dp * (dpi / 160)

dp = px * (160 / dpi)

그래도 지금 생각합니다 ... Brams 원래 수식에서 0으로 나누는 방법은 무엇입니까? displayMetrics.densityDpi는 120, 160, 240 또는 320이며 절대 0이 아닙니다.
ct_rob

2
(displayMetrics.densityDpi / 160)-이 부분은 작은 장치에서 0을 얻을 수 있습니다. 예를 들어 계산 120/160하면 두 값이 모두 int이므로 0이됩니다 (int) (px/0).

아, 네 말이 맞아 그래서 그가해야 할 일은 그의 공식에서 long을 사용하는 것입니다 : 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));
}

감사.


주어진 솔루션에 좋은 추가.
Bram

전환 계산이 적어도지도 조회만큼 빠르지 않고 동기화 된 것을 언급하지 않은 경우 놀랍습니다. 이 최적화가 유리하다는 것을 입증 할 테스트 결과가 있습니까? 작업 메모리가 제한되는 Android의 경우 계산 노력을 위해 메모리를 거래하는 것은 정당한 이유없이해야 할 일이 아닙니다.
Lorne Laliberte


2

아래 funtions는 여러 기기에서 잘 작동했습니다.

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

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

위에 허용 된 답변이 완전히 정확하지 않습니다. 안드로이드 소스 코드를 검사하여 얻은 정보에 따르면 :

Resources.getDimension()그리고 getDimensionPixelOffset()/ getDimensionPixelSize()는 전자가 돌아가고 float두 번째는 같은 값을 int적절히 반올림 한다는 점에서만 다릅니다 . 이들 모두에 대해 반환 값은 원시 픽셀입니다.

세 가지 함수는 모두 , 및 을 호출 Resources.getValue()하여 얻은 호출 및 변환 으로 구현됩니다 .TypedValueTypedValue.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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.