将像素转换为dp

956

我已经按像素为 Pantech 设备创建了应用程序,其分辨率为 480x800

我需要将高度和宽度转换为 G1 设备。
我认为将其转换为 dp 可以解决问题,并为两个设备提供相同的解决方案。

有没有简单的方法将像素转换为 dp?
有什么建议吗?


1
如果您想进行一次性转换(例如从Photoshop导出精灵),这里有一个不错的转换器 - Paul Lammertsma
3
pxdpsp 转换公式:在 Android 中,px 表示像素,是屏幕上的一个点;dp 表示密度无关像素,它会根据屏幕密度进行缩放;sp 表示可缩放像素,通常用于文本字号大小。以下是相互转化的公式:
  • px to dp: dp = px / (dpi / 160)
  • dp to px: px = dp * (dpi / 160)
  • sp to px: px = sp * (dpi / 160)
其中,dpi 是指每英寸包含的像素数。
- Suragch
http://web.archive.org/web/20140808234241/http://developer.android.com/guide/practices/screens_support.html#dips-pels - caw
35个回答

1159

Java代码:

// Converts 14 dip into its equivalent px
float dip = 14f;
Resources r = getResources();
float px = TypedValue.applyDimension(
    TypedValue.COMPLEX_UNIT_DIP,
    dip,
    r.getDisplayMetrics()
);

Kotlin 代码:

 val dip = 14f
 val r: Resources = resources
 val px = TypedValue.applyDimension(
     TypedValue.COMPLEX_UNIT_DIP,
     dip,
     r.displayMetrics
 )

Kotlin扩展:
fun Context.toPx(dp: Int): Float = TypedValue.applyDimension(
  TypedValue.COMPLEX_UNIT_DIP,
  dp.toFloat(),
  resources.displayMetrics)

410
注意:上述内容是将DIP转换为像素。原始问题是如何将像素转换为Dips! - Eurig Jones
13
以下是此问题的真实答案链接:https://dev59.com/nmw15IYBdhLWcg3wYKmo 。 - qix
140
有趣的是,当答案并不真正回答问题时,它更有帮助。-_- 我以为我想要问题所问的,然后我意识到我并不想要!所以很好的回答。我确实有一个问题。我怎么能获得applyDimension的最后一个参数?我可以只用 getResource().getDisplayMetrics() 吗,还是还有其他方法? - Andy
15
请注意:这是一个相对昂贵的操作。尝试缓存数值以加快访问速度。 - Entreco
11
如果无法访问 Context 对象,则使用 Resources.getSystem().getDisplayMetrics() - Farshad Tahmasbi
显示剩余5条评论

945
/**
 * This method converts dp unit to equivalent pixels, depending on device density. 
 * 
 * @param dp A value in dp (density independent pixels) unit. Which we need to convert into pixels
 * @param context Context to get resources and device specific display metrics
 * @return A float value to represent px equivalent to dp depending on device density
 */
public static float convertDpToPixel(float dp, Context context){
    return dp * ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT);
}

/**
 * This method converts device specific pixels to density independent pixels.
 * 
 * @param px A value in px (pixels) unit. Which we need to convert into db
 * @param context Context to get resources and device specific display metrics
 * @return A float value to represent dp equivalent to px value
 */
public static float convertPixelsToDp(float px, Context context){
    return px / ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT);
}

12
可能值得返回一个整数 Math.round(px) ,因为大多数方法都期望一个整数值。 - SingleWave Games
3
这是因为160 dpi(mdpi)是其他密度计算的基线密度。例如,hdpi被认为是mdpi密度的1.5倍,实际上就是240 dpi的另一种说法。有关所有密度,请参见Zsolt Safrany的答案。 - Stephen
20
根据Resource.getSystem()文档(重点标注为我的部分):返回一个全局共享的Resources对象,仅提供对系统资源(不包括应用程序资源)的访问,并且未针对当前屏幕进行配置(无法使用尺寸单位,不会随方向更改等)。 - Vicky Chijwani
2
开发人员可以选择向上取整/向下取整值。将控制权交给开发人员会更好。 - Muhammad Nabeel Arif
7
我建议使用DisplayMetrics.DENSITY_DEFAULT代替160f。参考链接:http://developer.android.com/reference/android/util/DisplayMetrics.html#DENSITY_DEFAULT - milcaepsilon
显示剩余12条评论

314
最好放在Util.java类中。
public static float dpFromPx(final Context context, final float px) {
    return px / context.getResources().getDisplayMetrics().density;
}

public static float pxFromDp(final Context context, final float dp) {
    return dp * context.getResources().getDisplayMetrics().density;
}

10
这并非在所有设备上都有效!使用此方法与使用TypedValue.applyDimension相比,在OnePlus 3T上的结果不同(可能是因为OnePlus在操作系统中内置了自定义缩放)。而使用TypedValue.applyDimension则能够在各种设备上保持一致的表现。 - Ben De La Haye

224
float density = context.getResources().getDisplayMetrics().density;
float px = someDpValue * density;
float dp = somePxValue / density;

density的值如下:

  • ldpi120 dpi)为.75
  • mdpi160 dpi;基准)为1.0
  • hdpi240 dpi)为1.5
  • xhdpi320 dpi)为2.0
  • xxhdpi480 dpi)为3.0
  • xxxhdpi640 dpi)为4.0

使用此在线转换器来尝试不同的dpi值。

编辑: 似乎dpi桶和density之间没有1:1的关系。例如Nexus 5Xxxhdpi,其density值为2.625(而非3)。您可以在设备指标中自行查看。


3
看起来 Nexus 5X 作为 xxhdpi 具有密度值为2.6(而不是3)。从技术上讲,Nexus 5X 是420dpi,Nexus 6/6P 是560dpi,两者都没有直接落入标准桶中,就像具有tvdpi(213dpi)的Nexus 7一样。因此,将这些设备列为xxdpi和xxxhdpi的网站是一个笑话。您的图表是正确的,并且这些设备将根据其“特殊”的dpi桶适当缩放。 - Steven Byle
谢谢提醒我如何获取一个dp中有多少像素。它是dpi/160(例如,在xxhdpi中为3px/dp)。 - CoolMind

144

您可以在没有上下文的情况下使用此内容(without Context)

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

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

就像@Stan提到的一样...使用这种方法可能会导致问题,如果系统改变了密度,请注意!

个人而言,我正在使用Context来做到这一点。这只是我想与您分享的另一种方法。


15
您可能不想使用这个。getSystem() 的文档 - “返回一个全局共享的 Resources 对象,提供对仅系统资源(无应用程序资源)的访问,并且未配置为当前屏幕(不能使用尺寸单位,不随方向改变等)。" - Stan
1
附议 @Stan 的观点:这很危险,你不应该使用它,特别是现在设备形态变得如此复杂的时候。 - Saket
1
这并不考虑可折叠设备和ChromeOS设备。 - EpicPandaForce

109

如果您可以使用dimensions XML,那么非常简单!

在您的res/values/dimens.xml中:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="thumbnail_height">120dp</dimen>
    ...
    ...
</resources>

然后在你的Java代码中:

getResources().getDimensionPixelSize(R.dimen.thumbnail_height);

由于px到dp取决于屏幕密度,除非OP在所有不同的屏幕尺寸上测试了px到dp方法,否则我不知道他或她是如何首先得到120的。 - John61590

78
根据Android开发指南所述:
px = dp * (dpi / 160)

但通常情况下,当您获得以像素为单位的设计时,您会希望执行相反的操作。所以:

dp = px / (dpi / 160)
如果您使用的是240dpi设备,则此比率为1.5(如前所述),这意味着60px的图标在应用程序中等于40dp。

2
Indhu,你可以参考以下链接:http://developer.android.com/guide/practices/screens_support.html#dips-pels以及http://developer.android.com/reference/android/util/DisplayMetrics.html#density - Sachchidanand
12
160是一个常数,答案正确。 - user25
3
@user25的回答非常好。你有没有阅读过有关Android屏幕的文档?当谈论Android屏幕密度时,“160”是一个普遍存在的常数。引用文档中的话:“中等密度(mdpi)屏幕(~160dpi)。 (这是基线密度)”。加油,伙计。 - mykolaj

74

没有上下文,优雅的静态方法:

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

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

31
Resources.getSystem() 的 javadoc 描述为“返回一个全局共享的 Resources 对象,仅提供对系统资源(而非应用资源)的访问,并且没有针对当前屏幕进行配置(不能使用尺寸单位,不会根据方向改变等)。” 这基本上意味着,即使它能够工作,你也不应该这样做。 - Austyn Mahoney
我刚刚读了@AustynMahoney的评论,意识到这个答案并不像我最初想的那么好,但是SO不允许我取消我的赞同! 哎呀! - Vicky Chijwani

69

使用Kotlin扩展可以使其更好

fun Int.toPx(context: Context): Int = (this * context.resources.displayMetrics.density).toInt()

fun Int.toDp(context: Context): Int = (this / context.resources.displayMetrics.density).toInt()

更新:

由于displayMetrics全局共享的Resources的一部分,因此我们可以使用Resources.getSystem()

val Float.toPx get() = this * Resources.getSystem().displayMetrics.density
    
val Float.toDp get() = this / Resources.getSystem().displayMetrics.density
    

    
val Int.toPx get() = (this * Resources.getSystem().displayMetrics.density).toInt()
    
val Int.toDp get() = (this / Resources.getSystem().displayMetrics.density).toInt()
    

提示:根据@EpicPandaForce的评论:

您不应该使用Resources.getSystem(),因为它无法处理可折叠设备和Chrome OS设备。


14
为什么要将其作为Int的扩展添加,该责任与Int类型无关。 - htafoya
@htafoya 感谢您的反馈。我在最新版本中考虑了您的提及。 - beigirad
1
你不应该使用 Resources.getSystem() 进行此操作,因为它无法处理可折叠设备和 Chrome OS 设备。 - EpicPandaForce

48

对于使用 Kotlin 的任何人:

val Int.toPx: Int
    get() = (this * Resources.getSystem().displayMetrics.density).toInt()

val Int.toDp: Int
    get() = (this / Resources.getSystem().displayMetrics.density).toInt()

使用方法:

64.toPx
32.toDp

从Resource.getSystem()的文档中:“返回一个全局共享的Resources对象,该对象提供对仅系统资源(无应用程序资源)的访问,并且未配置为当前屏幕(不能使用尺寸单位,不会根据方向更改等)。" - HendraWD
@HendraWD 文档可能会令人困惑,它提到的维度单位是您应用程序级别的dimens资源。displayMetrics 不是应用程序级别的资源。它是一个系统资源,并且返回正确的值。这段代码在我所有的生产应用中都能正常工作,从未出现过问题。 - Gunhan
我喜欢这个解决方案,但最初我把逻辑读反了。我想要输入dp值并说“myVal.dp”。如果你认为我的想法更好,那么你需要交换除法和乘法的逻辑。此外,我认为在这里使用roundToInt()toInt()稍微好一些。 - Adam Johns
@AdamJohns 因为所有与视图相关的函数都使用像素作为参数,所以我认为64.toPx更有意义。我将其解读为将64转换为像素。但是您可以随意更改顺序和名称。最重要的是最后是否乘以密度值。 - Gunhan
你不应该在这里使用 Resources.getSystem()。 - EpicPandaForce

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接