开发者

Get screen size bucket programmatically?

I want to determine whether the current device has a small, medium, large or xlarge screen in code. I can't find anything in the SDK docs that would help me get to that information. A开发者_如何学运维ll the methods / classes I have looked at provide only absolute values (i.e. screen size in pixels, screen density, etc.).

Is there a way to tell what kind of screen I'm running on in code?


I ended up using bool resources placed in the different bucket folders. I only needed to differentiate between normal (small / medium) and large (large / xlarge) screens, so I did this:

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>

Then, I just call getBoolean(R.bool.screen_large) to tell whether the screen is large or not. This way it's 100% up to the platform decide what screen the device has.


There is a difference between Density and screen type.

Get screen size bucket programmatically?

Since you can get the pixels and the density you can always have a static Helper class that determines that.

You can transofm pixels to dp with that

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

I think that you might want to add or subtract .5f from the pixels since getting pixels from dp comes from that code.

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

From the documentation

xlarge screens are at least 960dp x 720dp

large screens are at least 640dp x 480dp

normal screens are at least 470dp x 320dp

small screens are at least 426dp x 320dp


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


I agree that Felix answer is pretty elegant. But I think the way I found in this post might be even easier How to determine device screen size category (small, normal, large, xlarge) using code?


Use the DisplayMetrics class...

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

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

Small = DisplayMetrics.DENSITY_LOW

Medium = DisplayMetrics.DENSITY_MEDIUM

Large = DisplayMetrics.DENSITY_HIGH

XLarge = DisplayMetrics.DENSITY_XHIGH


I use this method in my code to differeniate between "Large Screens" (What I consider tablets) and "Small Screens" (What I consider phones).

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;
}       

The Configuration object also contains SCREENLAYOUT_SIZE_SMALL, SCREENLAYOUT_SIZE_XLARGE etc. (if you specifically need to test for size buckets).

This also works well with these two util functions:

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);
}

Where context.getResources().getDisplayMetrics().density is equal to 1.0, 1.5, 2.0 (mdpi, hdpi, xhdpi respectively).


Just as a complement to the Felix's answer, you can use the following code to get the ScreenSize without creating any folder for values-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");
        }


Other solutions here classify device screens into 4 buckets (small, normal, large, x-large).

For those looking to classify the device into the 6 drawable buckets instead, the following approach can be used.

The advantage of this approach is that it is not required to perform screen density calculations, which may differ from the way the OS does it. This solution instead relies purely on the OS to decide the density bucket. The disadvantage of this solution is that it requires creating redundant drawable resources, which is not elegant.

The are 6 screen density buckets:

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

The solution requires creating 6 drawables, and placing a copy into the density folders, so that each density folder is missing the correspondingly named resource. For example, pixel_not_ldpi.png, should be placed into all folders, except the drawable-ldpi folder.

resources

  1. pixel_not_ldpi.png // copy to the 5 folders, except drawable-ldpi
  2. pixel_not_mdpi.png // copy to the 5 folders, except drawable-mdpi
  3. pixel_not_hdpi.png // etc..
  4. pixel_not_xdpi.png // etc..
  5. pixel_not_xxdpi.png // etc..
  6. pixel_not_xxxdpi.png // etc..

Notes:

  1. Do not place any of these resources inside the general 'drawable' folder.
  2. It's best to create a 1x1 pixel png image, which is only 68kb, and is smaller than a 1x1 jpeg image. Also make sure to strip meta data from these images to save space.

Once the images are placed, write a helper method which attempts to load each of these resources using 6 try/catch blocks. Catch the Resources.NotFoundException. Whichever resource throws the exception, that is the screen density of the device. This works with Android App Bundles.

It's not an ideal solution, but it answers the question comprehensively, and unlike other solutions, which involve arithmetic, it relies purely on the OS to determine density.

Edge Case: incorrect side-loading of App Bundle

Some users side-load their apps, by downloading them from outside the play store, or otherwise, and can make the mistake of downloading only a part of the resources present in the whole app bundle. Such users are able to install and launch the app, but some resources might be missing, which would cause a resource not found exception to fire inside one of our try/catch blocks above, making it impossible to determine whether the resource was missing intentionally, or unintentionally (due to an incomplete app bundle).

To differentiate the two cases, simply use the same approach. Place another resource, pixel.png into all 6 density drawable folders, and try to load it using a try/catch. If it throws, then the app was downloaded incorrectly.

Enjoy


The DisplayMetrics has got all that you need. To get it

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜