在安卓系统中如何以编程方式获取屏幕密度?

560

如何在Android中以编程方式获取屏幕密度?

我的意思是:如何查找当前设备的屏幕dpi?


10
很多答案提到了getDisplayMetrics().xdpi,它应该返回设备的实际 dpi 值。请注意,由于制造商没有一致正确地设置这个值,因此您不能仅仅依赖它们。可悲而真实的是:关于实际 dpi 的信息不可用。来源:https://groups.google.com/d/msg/android-developers/g56jV0Hora0/9d8p8QJg1ksJ - sulai
2
getResources().getDisplayMetrics().xdpigetResources().getDisplayMetrics().ydpi 将会给你实际的水平和垂直密度,在大多数情况下是不同的。 - Ωmega
21个回答

565

您可以从DisplayMetrics结构中获取有关显示的信息:

DisplayMetrics metrics = getResources().getDisplayMetrics();
尽管Android不使用直接的像素映射,但它使用一些量化的独立像素密度值,然后缩放到实际屏幕大小。因此,metrics.densityDpi属性将是DENSITY_xxx常量之一(120160213240320480640 dpi)。
如果您需要实际的液晶像素密度(例如OpenGL应用程序),则可以从水平和垂直密度分别获取metrics.xdpimetrics.ydpi属性。
如果您的目标是早于API Level 4,则metrics.density属性是从参考密度(160dpi)的浮点缩放因子。现在由metrics.densityDpi提供相同的值可以进行计算。
int densityDpi = (int)(metrics.density * 160f);

50
虽然这篇帖子已经发表超过两年了,但它是在谷歌搜索中的排名最高的,所以对于任何找到它的人来说,你不再需要乘以160了。 - user766650
7
从哪个版本开始不再支持? - TacB0sS
8
请注意:您可能更喜欢使用这个更新的API:getWindowManager().getDefaultDisplay().getRealMetrics(metrics); 官方在API 17中添加了它,但是我很惊讶地发现,即使在我尝试过的4.0设备上,它也能够正常工作。 - benkc
5
还有getResources().getDisplayMetrics().densityDpi。 - amorenew
1
现在 'getDefaultDisplay()'已经过时,而 'getMetrics(android.util.DisplayMetrics)'也已经过时,请帮助我。 - Prabhakaran P
显示剩余4条评论

413

这也可以正常工作:

 getResources().getDisplayMetrics().density;

这将给你:

0.75 - ldpi

1.0 - mdpi

1.5 - hdpi

2.0 - xhdpi

3.0 - xxhdpi

4.0 - xxxhdpi

这里输入图像描述

参考:密度

这里输入图像描述

参考2


11
当你无法直接访问WindowManager(例如在Loader内部)时,可以使用此方法。只需将其乘以160即可。 - Michał Klimczak
2
API Level 16 添加了 xxdpi,这里对应的是 3.0。 - QED
4
这将为tvdpi提供1.3312501。有关tvdpi的更多信息,请参见此处 - Dori
2
Nexus 7 报告 1.3,为什么括号应该放在哪里? - Neil
1
@selbie,你的手机可能正在拉取和缩放更高质量的资源,因为你的手机报告的密度正好介于两个定义的密度之间。 - Sakiboy
显示剩余5条评论

149
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);

switch(metrics.densityDpi) {
     case DisplayMetrics.DENSITY_LOW:
         break;

     case DisplayMetrics.DENSITY_MEDIUM:
         break;

     case DisplayMetrics.DENSITY_HIGH:
         break;
}

这将适用于API级别4及更高级别。


你如何处理像 Nexus 7 这样报告 densityDpi 为 213 的设备? - Neil
请在像这样的密度值之间进行if-else手动检查,例如:if (metrics.densityDpi > DisplayMetrics.DENSITY_LOW && metrics.densityDpi < DisplayMetrics.DENSITY_MEDIUM) - Mitul Nakum
1
如果(metrics.densityDpi < DisplayMetrics.DENSITY_LOW){} else if (metrics.densityDpi < DisplayMetrics.DENSITY_MEDIUM){} ... - Mitul Nakum
1
Neil,213 被称为 TV DPI,对应的显示度量密度为 DENSITY_TV。 - Andrew S

64

Blundell 的答案作为静态帮助方法:

private static String getDensityName(Context context) {
    float density = context.getResources().getDisplayMetrics().density;
    if (density >= 4.0) {
        return "xxxhdpi";
    }
    if (density >= 3.0) {
        return "xxhdpi";
    }
    if (density >= 2.0) {
        return "xhdpi";
    }
    if (density >= 1.5) {
        return "hdpi";
    }
    if (density >= 1.0) {
        return "mdpi";
    }
    return "ldpi";
}

1
那么关于tvdpi密度呢?我猜它是1.33。 - Anoop
@AnoopssGolden 这不是真正的标准,所以如果您想添加它,可以这样做,但我认为答案不应该包括它。根据Android文档:“这不被视为“主要”密度组。它主要用于电视,大多数应用程序不需要它”。 - qwertzguy
1
我知道这有点老了,但是只是想补充一下;MDPI 可能适用于 Nexus 7 上的大多数事情,但是现在我正在尝试的是,以 MDPI 形式下载的图像不够大。我必须定义 TVDPI,然后从服务器请求更大的图像尺寸。它可能不经常使用,但这并不意味着 MDPI 将覆盖所有内容。 - RED_
那段代码需要一些else语句。希望你不介意,我添加了它们并删除了大括号。现在稍微更加高效了。 - Andrew S
3
嘿 @Andrew S,使用“else”不会影响代码的效率,因为每个“if”都会立即返回。去掉花括号只是一个风格问题,在维护代码时容易出错,这是我的个人看法。 - qwertzguy
显示剩余4条评论

48
以下是一些密度常数,来源

enter image description here

除了标准密度外,还有5个中间密度。考虑到这一事实,下面的代码将是一个完整的工作示例:
float density = getResources().getDisplayMetrics().density;

if (density == 0.75f)
{
    // LDPI
}
else if (density >= 1.0f && density < 1.5f)
{
    // MDPI
}
else if (density == 1.5f)
{
    // HDPI
}
else if (density > 1.5f && density <= 2.0f)
{
    // XHDPI
}
else if (density > 2.0f && density <= 3.0f)
{
    // XXHDPI
}
else
{
    // XXXHDPI 
}

或者,您可以使用densityDpi来查找密度常量:

int densityDpi = getResources().getDisplayMetrics().densityDpi;

switch (densityDpi)
{
    case DisplayMetrics.DENSITY_LOW:
        // LDPI
        break;

    case DisplayMetrics.DENSITY_MEDIUM:
        // MDPI
        break;

    case DisplayMetrics.DENSITY_TV:
    case DisplayMetrics.DENSITY_HIGH:
        // HDPI
        break;

    case DisplayMetrics.DENSITY_XHIGH:
    case DisplayMetrics.DENSITY_280:
        // XHDPI
        break;

    case DisplayMetrics.DENSITY_XXHIGH:
    case DisplayMetrics.DENSITY_360:
    case DisplayMetrics.DENSITY_400:
    case DisplayMetrics.DENSITY_420:
        // XXHDPI
        break;

    case DisplayMetrics.DENSITY_XXXHIGH:
    case DisplayMetrics.DENSITY_560:
        // XXXHDPI
        break;
}

45

试试这个:

DisplayMetrics dm = context.getResources().getDisplayMetrics();
int densityDpi = dm.densityDpi;

5
我更喜欢这个,因为它依赖于上下文而不是活动。 - greg7gkb
同意,我可以更容易地从“视图”中使用它(这也是我需要它的地方!) - Andrew Wyld

38

获取dpi:

DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);

// will either be DENSITY_LOW, DENSITY_MEDIUM or DENSITY_HIGH
int dpiClassification = dm.densityDpi;

// these will return the actual dpi horizontally and vertically
float xDpi = dm.xdpi;
float yDpi = dm.ydpi;

4
dm.densityDpi返回DENSITY_LOW、DENSITY_MEDIUM或DENSITY_HIGH之一。那么xhdpi呢?是否有DENSITY_XHIGH或类似的值? - Eugene Chumak
是的,有:http://developer.android.com/reference/android/util/DisplayMetrics.html - caw

29
以下答案是基于 qwertzguy 的答案做了一些小的改进。
double density = getResources().getDisplayMetrics().density;
if (density >= 4.0) {
   //"xxxhdpi";
}
else if (density >= 3.0 && density < 4.0) {
   //xxhdpi
}
else if (density >= 2.0) {
   //xhdpi
}
else if (density >= 1.5 && density < 2.0) {
   //hdpi
}
else if (density >= 1.0 && density < 1.5) {
   //mdpi
}

1
这段代码可以加上一些其他的东西,而且它后面的和条件中的“&”是多余的。 - Andrew S
1
@Andrew,感谢您的纠正。我已经相应地编辑了答案。 - San
2
@San 在这些条件中,您不需要使用&&,只要您始终检查是否>=,else if将终止为第一个为真的条件。 - dbenson
3
这对我来说没有返回正确的结果,我使用的是我的 Nexus 5X(顺便说一句,这是一个默认的谷歌设备)。设备密度为 xxhdpi,而返回的双倍密度约为 2.6。 - Tobliug

21

实际上,如果您想要获得真实的显示器dpi,答案应该在获取显示器度量信息时介于两者之间:

DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
int dpiClassification = dm.densityDpi;
float xDpi = dm.xdpi;
float yDpi = dm.ydpi;

densityDpi * 160将为您提供值/建议,告诉您应该使用哪种密度

0.75 - ldpi - 120 dpi
1.0 - mdpi - 160 dpi
1.5 - hdpi - 240 dpi
2.0 - xhdpi - 320 dpi
3.0 - xxhdpi - 480 dpi
4.0 - xxxhdpi - 640 dpi

如之前的帖子所述

但是 dm.xdpi 不会总是给出给定显示器的实际dpi

例如:

Device: Sony ericsson xperia mini pro (SK17i)
Density: 1.0 (e.g. suggests you use 160dpi resources)
xdpi: 193.5238
Real device ppi is arround 193ppi


Device: samsung GT-I8160 (Samsung ace 2)
Density 1.5 (e.g. suggests you use 240dpi resources)
xdpi 160.42105
Real device ppi is arround 246ppi

所以也许显示器的实际dpi应该是Density*xdpi,但我不确定这是否是正确的方法!


1
在过去的6个月中,我所有在Google Play上发布的应用程序中,使用Density*xdpi一直运行得非常完美。 - Marek Halmo
密度 * xdpi的乘法不是合理的,现在有更多高密度设备,这一点更清晰了 - 密度和xdpi都增加了,所以将它们相乘会使增加计数加倍。我认为三星ace 2是由供应商出错了。Android规范指出,xdpi和ydpi是真实像素密度 - 不要乘以任何东西。 - ToolmakerSteve

18

这应该有助于您的活动...

void printSecreenInfo(){

    Display display = getWindowManager().getDefaultDisplay();
    DisplayMetrics metrics = new DisplayMetrics();
    display.getMetrics(metrics);

    Log.i(TAG, "density :" +  metrics.density);

    // density interms of dpi
    Log.i(TAG, "D density :" +  metrics.densityDpi);

    // horizontal pixel resolution
    Log.i(TAG, "width pix :" +  metrics.widthPixels);

     // actual horizontal dpi
    Log.i(TAG, "xdpi :" +  metrics.xdpi);

    // actual vertical dpi
    Log.i(TAG, "ydpi :" +  metrics.ydpi);

}

输出:

I/test( 1044): density :1.0

I/test( 1044): D density :160

I/test( 1044): width pix :800

I/test( 1044): xdpi :160.0

I/test( 1044): ydpi :160.42105

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