6

Can somebody tell me how Android counts the screen density?

My problem is I have a device (ODYS Space) with 480x800 resolution and with 7" diagonal screen. If I calculate its density I get a value 133 DPI but Android (2.2 and 2.3 too) reports it like "MEDIUM" density device (160 DPI).

I'm fighting with multiscreen support so I supposed 133 DPI will be rather reported like "LOW" than "MEDIUM" so now my screen layout looks quite stupid on this medium reported device.

I check the device with code like this:

DisplayMetrics dMetrics = new DisplayMetrics(); 
getWindowManager().getDefaultDisplay().getMetrics(dMetrics);  
int d=dMetrics.densityDpi;

If I run that code on a virtual device configured (480x800/7" and 133 DPI) then I got density=120.

On the real device why does it say 160 instead?

Peter O.
  • 32,158
  • 14
  • 82
  • 96
rugo
  • 71
  • 1
  • 1
  • 3
  • If you're looking for the bounty, see Peter O.'s answer and my comment below it. The main point here is how Android actually calculates the density, not how it retrieves the settings. For example, where is the cut-off between LDPI and MDPI, MDPI and HDPI, and so on… why a given number is rounded to the upper density and not the lower adjacent density, and so on. Again, a hint of the answer can be seen in the AVD Device Editor, but so far I haven't been able to find a deterministic, certain, correct answer. – davidcesarino Sep 18 '14 at 02:34
  • DPI - Device independent pixel You cannot have custom values because there are starndards 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 – Georgian Benetatos Sep 20 '14 at 15:46

6 Answers6

4

There are two different things here.

  1. The behavior in the emulator which is a combination of the AVD manager configuring the AVD itself and maybe using a device definition. The emulator system images have baked in values so that we can ship the same image for all device configurations. This baked in value is mdpi for density. When you create an AVD with a different density we inject just before boot time the new value. The injected value is converted to a density bucket value (ldpi, mdpi, hdpi, ...) based on basic rules (if you are past the half point between bucket value, you go in the next value).

So the half point between 120 and 160 is 140, and therefore 133dpi -> ldpi.

  1. Device do whatever they want. It's a manual process for any OEM to decide what their device's bucket value is, and this get set in a property. It is not dynamically computed based on the actual hardware screen size of the device. You can do a device that has a true screen density of 133 and yet put it in the xxdpi bucket if you want.

The end result is that you need to create a new device definition where you manually say your 7" 480x800 device is actually a medium density device, and it should work. If it doesn't, it's a bug on our side when we configure the emulator for a particular device-based AVD. It's not an issue on the Android platform itself which doesn't compute anything.

Xavier Ducrohet
  • 28,383
  • 5
  • 88
  • 64
  • Sure, I knew OEMs defined their own value (similar to how the modding community does, I guess). About the AVD editor, I didn't know _exactly_ how the dialog calculated the density (not seeing the code). That said, thanks for confirming what I suspected by experimenting: that is selects the bucket that is the nearest to the calculated density. With that information, I went to find where exactly it was located and found `SizeListener` class in `DeviceCreationDialog` (in sdkuilib), which gave me the exact algorithm used in that circumstance. Thank you. – davidcesarino Sep 21 '14 at 00:43
2

I have updated one of the other solutions for 2014.

Call this method in one of your Activities:

private void tellMeDensity() {
        DisplayMetrics dm = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(dm);
        int dpiClassification = dm.densityDpi;

        float xDpi = dm.xdpi;
        float yDpi = dm.ydpi;

        Toast.makeText(this, "xdpi=" + xDpi, Toast.LENGTH_SHORT).show();
        Toast.makeText(this, "ydpi=" + yDpi, Toast.LENGTH_SHORT).show();

        switch(dpiClassification) {
           case DisplayMetrics.DENSITY_LOW:
               Toast.makeText(this, "low density", Toast.LENGTH_SHORT).show();
               break;    
           case DisplayMetrics.DENSITY_MEDIUM:
               Toast.makeText(this, "medium density", Toast.LENGTH_SHORT).show();
               break;                
           case DisplayMetrics.DENSITY_HIGH:
               Toast.makeText(this, "high density", Toast.LENGTH_SHORT).show();
               break;    
           case DisplayMetrics.DENSITY_XHIGH:
               Toast.makeText(this, "xhigh density", Toast.LENGTH_SHORT).show();
               break;                  
           case DisplayMetrics.DENSITY_XXHIGH:
               Toast.makeText(this, "xxhigh density", Toast.LENGTH_SHORT).show();
               break;                  
           case DisplayMetrics.DENSITY_XXXHIGH:
               Toast.makeText(this, "xxxhigh density", Toast.LENGTH_SHORT).show();
               break;      
        }
    }
Jason
  • 1,658
  • 3
  • 20
  • 51
Tyler
  • 17,669
  • 10
  • 51
  • 89
0

The manufacturer chooses the density when it makes the ROM image for your device. It is not calculated at run-time.

If you take a look at the source: https://github.com/android/platform_frameworks_base/blob/master/core/java/android/util/DisplayMetrics.java#L294 you'll see the getDeviceDensity() function tries to use two system properties one is the qemu emulator value qemu.sf.lcd_density, and the other is the manufacturer set value ro.sf.lcd_density, and finally if the manufacturer forgot to set one the system falls back to the default. DENSITY_DEFAULT is set to DENSITY_MEDIUM which is set to 160.

You can verify the device property by plugging in your device and running this command:

adb shell getprop ro.sf.lcd_density

The properties are stored in /system/build.prop and are loaded on boot. You can look at the contents of the file with this command:

adb shell cat /system/build.prop
Simon
  • 10,932
  • 50
  • 49
0

If you check the docs (look for the "Using configuration qualifiers" section), a device is considered "low DPI" until you get around/below 120 DPI.

dmon
  • 30,048
  • 8
  • 87
  • 96
  • Yeah. androis says 120 = LOW; 160 = MEDIUM; 240 = HIGH. it is OK (also I know it use any rounding when calculatesthese standard values) – rugo Jan 29 '12 at 17:17
  • 1
    BUT why android rounds 133 DPI to 160 opposite 120 as I expected? – rugo Jan 29 '12 at 17:17
  • I'm sure they had good reasons for it, but I don't know them. – dmon Jan 29 '12 at 17:27
0

I'm using same code and I have seen values 160, 240, 320 densityDpi values only. I thinks it's such a normalization on android os. It's my suggestion only I don't know detailed technical information.

lulumeya
  • 1,619
  • 11
  • 14
0
**dpi calculation programitically:**

public class SampleActivity extends Activity
{

   @Override
   public void onCreate(Bundle savedInstanceState)
    {
      super.onCreate(savedInstanceState);
       setContentView(R.layout.main);
       DisplayMetrics dm = new DisplayMetrics();
       getWindowManager().getDefaultDisplay().getMetrics(dm);
       int dpiClassification = dm.densityDpi;

    float xDpi = dm.xdpi;
    float yDpi = dm.ydpi;

    Toast.makeText(SampleActivity.this, "xdpi="+xDpi, Toast.LENGTH_SHORT).show();
    Toast.makeText(SampleActivity.this, "ydpi="+yDpi, Toast.LENGTH_SHORT).show();


    switch(dpiClassification)
    {
       case DisplayMetrics.DENSITY_LOW:
           Toast.makeText(SampleActivity.this, "low density",    
                   Toast.LENGTH_SHORT).show();

                 break;

       case DisplayMetrics.DENSITY_MEDIUM:
           Toast.makeText(SampleActivity.this, "low medium", 
                  Toast.LENGTH_SHORT).show();

                 break;
       case DisplayMetrics.DENSITY_HIGH:
           Toast.makeText(SampleActivity.this, "low high", 
                     Toast.LENGTH_SHORT).show();

                  break;



       case DisplayMetrics.DENSITY_XHIGH:
           Toast.makeText(SampleActivity.this, "low xhigh", 
                  Toast.LENGTH_SHORT).show();

                 break;
       }

    }
 }
Amey Haldankar
  • 2,223
  • 1
  • 24
  • 22