以编程方式获取屏幕尺寸桶?

20

我想在代码中确定当前设备屏幕的大小,包括小、中、大或超大。在SDK文档中没有找到任何有助于获得这些信息的内容。我查看的所有方法/类都只提供绝对值(即像素屏幕尺寸、屏幕密度等)。

是否有一种方式可以在代码中告诉我当前运行的屏幕类型?


2
我不确定你在做什么,但值得一提的是,尽量避免这样的行为。如果可以的话,请创建各种资源,让 Android 自己决定应该加载哪个资源。 - Ian
@Ian 我知道这是应该避免的事情,但在这种情况下我别无选择。我需要根据屏幕大小为对话框设置不同的窗口标志。 - Felix
好的。没问题。我只是想提一下。 - Ian
1
可能是一个与之前的问题类似: 如何使用代码确定设备屏幕尺寸类别(小、正常、大、特大)?(链接:https://dev59.com/-G445IYBdhLWcg3wJ258) - Pang
9个回答

58

最终我使用了放置在不同桶文件夹中的bool资源。我只需要区分普通(小/中)和大型(大/超大)屏幕,所以我这样做:

values/bools.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <bool name="screen_large">false</bool>
</resources>

values-large/bools.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <bool name="screen_large">true</bool>
</resources>

然后,我只需要调用getBoolean(R.bool.screen_large) 来判断屏幕是否大屏幕。这样,完全由平台决定设备的屏幕大小。


5
这很酷!这是我见过的最优雅的黑客技巧 :)。 - Ian
5
+1我不知道为什么这个答案让我笑了。:)) 很好! - Shardul
+1 绝对是一个非常巧妙的答案,太棒了 :) 即使在 Nexus 7 上也可以运行! - Soham
女孩?我的名字是Felix。那是一个男孩的名字 :-/ - Felix
3
有人请给这个人一枚奖章。纯天才的黑客。 - Aaron
1
@Aaron 我认为安卓金徽章已经足够作为一枚奖章了 :)。 - Felix

7

密度和屏幕类型是有区别的。 enter image description here

由于您可以获取像素和密度,因此您始终可以使用静态 Helper 类来确定它们。

您可以使用该类将像素转换为 dp。

public static float dpFromPixels(int pixels) {
        float dp = (float) ((pixels) / Density.scale);
        return dp;
    }

我认为您可能需要在像素值上加或减0.5f,因为从dp获取像素值是基于该代码的。

public static int pixelsFromDp(float dps) {
        int pixels = (int) (dps * Density.scale + 0.5f);
        return pixels;
    }

以下是来自文档的内容:

超大屏幕至少为 960dp x 720dp

大屏幕至少为 640dp x 480dp

普通屏幕至少为 470dp x 320dp

小屏幕至少为 426dp x 320dp


值得注意的是,将屏幕放入桶中时,最小的尺寸被视为标准。 - Ian
1
+1,好的解决方案,我忘记他们实际上发布了尺寸。不过,我选择了一个不太硬编码的解决方案,请查看我的答案 :) - Felix
1
您不能使用上述分类,因为它没有清楚地指定屏幕尺寸的边界。 - Ron
Felix提到他可以访问“屏幕尺寸(以像素为单位)、屏幕密度等信息”,因此他能够确定屏幕类型。 - weakwire
Density.scale 是从哪里来的? - Qwertie

6
public static boolean isLargeScreen(Context context) {
  int screenSize = context.getResources().getConfiguration().screenLayout
    & Configuration.SCREENLAYOUT_SIZE_MASK;
  return screenSize >= Configuration.SCREENLAYOUT_SIZE_LARGE;
}

5

非常酷,但不适合(没有hack的情况下)x-large设备。 - Dmitry Zaytsev

1

使用 DisplayMetrics 类...

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

// DENSITY_LOW, DENSITY_MEDIUM, DENSITY_HIGH, or DENSITY_XHIGH
int density = dm.densityDpi;

小 = DisplayMetrics.DENSITY_LOW

中 = DisplayMetrics.DENSITY_MEDIUM

大 = DisplayMetrics.DENSITY_HIGH

特大 = DisplayMetrics.DENSITY_XHIGH


真是讨厌,有些人会点踩却不解释原因... 我经常使用这个来显示特定的布局,效果非常好。 - DRiFTy
1
我给你的回答点了踩,因为它没有回答我的问题。它只给出了屏幕密度而非尺寸。建议你了解一下这两者之间的区别。 - Felix
1
DisplayMetrics确实给出了像素大小。可能你需要撤销你的投票。 - Ron
@user7777777777,我认为他要么问了一个与他想要的不同的问题,要么根本没有尝试我们的任何答案来解决他的问题。 - DRiFTy
@Kieran:你的屏幕密度映射有误,因为可能会出现xlarge屏幕具有高密度的情况。 - Ron
显示剩余2条评论

1

作为对Felix答案的补充,您可以使用以下代码获取屏幕尺寸,而无需创建任何值-XXX文件夹:

int screenSize = getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
        Log.d(TAG, "screenSize = "+screenSize);

        switch(screenSize) {
            case Configuration.SCREENLAYOUT_SIZE_LARGE:
                Log.d(TAG, "Large Screen");
                break;
            case Configuration.SCREENLAYOUT_SIZE_NORMAL:
                Log.d(TAG, "Normal Screen");
                break;
            case Configuration.SCREENLAYOUT_SIZE_SMALL:
                Log.d(TAG, "Small Screen");
                break;
            case Configuration.SCREENLAYOUT_SIZE_XLARGE : 
                Log.d(TAG, "XLarge Screen");
                break;
            default:
                Log.d(TAG,  "Screen size is neither large, normal or small or xlarge");
        }

1
我在我的代码中使用这种方法来区分“大屏幕”(我认为是平板电脑)和“小屏幕”(我认为是手机)。
public static boolean isLargeScreen(Configuration toCheckConfig) {  
    int screenSizeBucket = toCheckConfig.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
    if (screenSizeBucket >= Configuration.SCREENLAYOUT_SIZE_LARGE) {
        return true;
    }
    else return false;
}       

Configuration对象还包含SCREENLAYOUT_SIZE_SMALLSCREENLAYOUT_SIZE_XLARGE等(如果您需要特别测试大小桶)。

这也可以很好地与这两个实用函数配合使用:

public static int getPixelsFromDp(Context context, int dpValue) {
    return (int) (context.getResources().getDisplayMetrics().density * dpValue);
}

public static int getDpfromPixels(Context context, int pixels) {
    return (int) (pixels / context.getResources().getDisplayMetrics().density);
}

context.getResources().getDisplayMetrics().density 等于 1.0, 1.5, 2.0 时,分别代表着 mdpi, hdpi, xhdpi


0

其他方案将设备屏幕分类为4个桶(小、正常、大、特大)。

对于那些想要将设备分类为6个可绘制桶的人,可以使用以下方法。

这种方法的优点是不需要执行屏幕密度计算,因为这可能与操作系统的计算方式不同。相反,该解决方案纯粹依赖于操作系统来决定密度桶。这种解决方案的缺点是需要创建冗余的可绘制资源,这并不优雅。

有6个屏幕密度桶:

  1. drawable-ldpi
  2. drawable-mdpi
  3. drawable-hdpi
  4. drawable-xhdpi
  5. drawable-xxhdpi
  6. drawable-xxxhdpi

该解决方案需要创建6个可绘制图像,并将其放置到密度文件夹中的一个副本中,以便每个密度文件夹都缺少相应命名的资源。例如,pixel_not_ldpi.png应放置在所有文件夹中,除了drawable-ldpi文件夹。

资源

  1. pixel_not_ldpi.png // 复制到5个文件夹,除了drawable-ldpi
  2. pixel_not_mdpi.png // 复制到5个文件夹,除了drawable-mdpi
  3. pixel_not_hdpi.png // 等等..
  4. pixel_not_xdpi.png // 等等..
  5. pixel_not_xxdpi.png // 等等..
  6. pixel_not_xxxdpi.png // 等等..

注意:

  1. 不要将这些资源放在通用的“drawable”文件夹中。
  2. 最好创建一个1x1像素png图像,它只有68kb,比1x1 jpeg图像更小。还要确保从这些图像中剥离元数据以节省空间。

一旦放置了这些图像,请编写一个帮助方法,使用6个try/catch块尝试加载每个资源。捕获Resources.NotFoundException。无论哪个资源引发异常,都是设备的屏幕密度。这适用于Android应用程序包。

这不是理想的解决方案,但它全面回答了问题,并且与其他涉及算术的解决方案不同,它纯粹依赖于操作系统来确定密度。

边界情况:App捆绑包未正确侧载

一些用户通过从Play商店之外或其他途径下载应用程序进行侧载,但有时会犯下载仅部分资源而非完整应用程序捆绑包的错误。这些用户可以安装并启动应用程序,但某些资源可能会丢失,这将导致在我们上面的try/catch块中触发资源未找到异常,从而无法确定资源是故意缺失还是因为不完整的应用程序捆绑包而丢失。

为了区分这两种情况,只需要使用相同的方法。将另一个资源pixel.png放入所有6个密度可绘制文件夹中,并尝试使用try/catch加载它。如果抛出异常,则说明应用程序已被错误下载。

享受吧!


-1

DisplayMetrics 包含了你所需要的一切。要获取它,

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

1
DisplayMetrics只是告诉你屏幕的密度,它并不能告诉你屏幕的类型。如果我说错了,请纠正我。 - weakwire
你错了。它还有 heightPixelswidthPixels,它们给出了显示器的绝对高度和宽度(以像素为单位)。 - Ron
嗯,它还提供了其他信息。我的观点是你没有为他的问题提供任何解决方法。当然,他将使用DisplayMetrics来获取像素数密度,但这些值本身无法确定屏幕类型。另外,如果你认为我给你投了反对票而给我投了反对票,那就不是这样的情况。 - weakwire
@weakwire:我没有。而且我知道是所有者给它投了反对票或取消了投票。 - Ron

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