公式px到dp,dp到px android


150

我正在尝试计算可变数量的像素到与密度无关的像素,反之亦然。

该公式(px to dp): dp = (int)(px / (displayMetrics.densityDpi / 160));不适用于小型设备,因为它被零除。

这是我的dp至px公式:

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

有人可以给我一些指示吗?



@布拉姆:我认为你的公式很好。您将如何除以零?displayMetrics.densityDpi将或者120,160,240或320,从未0
ct_rob

我同意@ct_rob。displayMetrics.densityDpi / 160的最小值将为0.75。您必须将int强制转换为错误的位置。
TomTaila

Answers:


318

注意:上面广泛使用的解决方案基于displayMetrics.density。但是,文档说明此值是舍入值,与屏幕“存储桶”一起使用。例如。在我的Nexus 10上,它返回2,其中实际值为298dpi(真实)/ 160dpi(默认)= 1.8625。

根据您的要求,您可能需要进行精确的转换,可以像这样实现:

[编辑]这并不是要与Android的内部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输入,则在那里将无法使用。
巴基2014年

1
xdpi和ydpi不应使用,因为在许多设备上有时甚至很多,它们都不准确。仅DisplayMetrics.densityDpi是可靠的,这是不幸的,因为它在设计上不精确。有关更多信息,请参见Google论坛主题:groups.google.com/forum/#!topic/android
Mark McClelland

1
我找到了这个响应,并实际使用了许多类似的解决方案,但我只是浏览了一下文档,发现具有给定尺寸(在dip / dp中声明)的getDimensionPixelOffSet像手工制作的代码一样以像素为单位返回了偏移量。我进行了完美的测试和工作。希望能帮助到你!
william gouvea 2014年

1
h,这没有给出正确的值。尝试:资源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值,使它截断尾数并将特性保留为正确舍入的整数值。
凯利·科普利2012年

3
这并不总是正确的。当我有两个布局时,一个在另一个内,然后我在每个视图的角上倒圆角(一个通过dp,其他视图转换成dp),角不匹配!
Marek 2013年

我对此技术没有任何问题。我将其用于不同的布局,并且始终可以按预期工作。当然,我几年前就使用过,但不确定是否仍然有效。也许您可以在PanaVTEC的答案中尝试其他技术。也可能是圆角不仅仅是dp / px计算。
Bram 2013年

5
这与Android开发人员网站上采用的解决方案相同,所以我认为它是正确的:)。http://developer.android.com/guide/practices/screens_support.html#dips-pels
alocaly 2014年

1
+1谢谢。像魅力一样工作!感谢您与我们分享此解决方案。
Simon Dorociak 2014年

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_DIPvalue * metrics.density则会返回实际需要的位置value * metrics.density。所以,你是什么getting` valueInDp`是不是在dpvalueInPxpx
bitbybit

^ typo,我的意思是“ ...您实际需要的地方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
更好,因为不需要使用Context :)谢谢。
Ayaz Alifov

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()相同,除了将返回值转换为整数像素以用作大小。大小转换涉及四舍五入基础值,并确保非零基础值的大小至少为一个像素。
CJBS

14

使用此功能

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)

但是,现在,我考虑一下……在Brams原始公式中怎么会除以零?displayMetrics.densityDpi将或者120,160,240或320,从未0
ct_rob

2
(displayMetrics.densityDpi / 160)-这部分在小型设备上可以为0,例如120/160,这两个值均为int,结果为0。最终为(int) (px/0)

啊,你是对的。因此,他要做的就是在其公式中使用一个长
整数

该答案与上一个答案相似,因为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'8


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

//用于获取小数/浮点数

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适当舍入后的相同值。对于所有这些,返回值以原始像素为单位。

所有三个功能是通过调用implementedy 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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.