安卓屏幕密度计算

6
有人能告诉我Android如何计算屏幕密度吗?
我的问题是我有一个480x800分辨率和7英寸对角线屏幕的设备(ODYS Space)。 如果我计算它的密度,我得到一个值为133 DPI,但Android(2.2和2.3也是如此)报告它像“中等”密度设备(160 DPI)。
我正在解决多屏幕支持的问题,所以我想133 DPI会被认为比“中等”更像“低”,因此现在我的屏幕布局在这个中等报告的设备上看起来相当愚蠢。
我使用以下代码检查设备:
DisplayMetrics dMetrics = new DisplayMetrics(); 
getWindowManager().getDefaultDisplay().getMetrics(dMetrics);  
int d=dMetrics.densityDpi;

如果我在配置了480x800/7英寸和133 DPI的虚拟设备上运行该代码,则得到density=120。
那么为什么在真实设备上会显示160呢?

如果你在寻找赏金,可以看看Peter O.的回答以及我在下面的评论。这里的重点是Android实际如何计算密度,而不是如何检索设置。例如,在LDPI和MDPI之间的截止点在哪里,MDPI和HDPI之间的截止点在哪里等等...为什么给定的数字会被舍入到更高的密度而不是更低的相邻密度等等。同样,答案的线索可以在AVD设备编辑器中看到,但到目前为止,我还没有找到一个确定、确切、正确的答案。 - davidcesarino
DPI - 设备无关像素你不能使用自定义值,因为有标准 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
6个回答

4
这里有两个不同的事情。
1.模拟器中的行为是AVD管理器配置AVD本身和可能使用设备定义的组合。模拟器系统映像具有固定值,以便我们可以为所有设备配置提供相同的映像。这个固定值是密度为mdpi。
当您创建具有不同密度的AVD时,我们会在启动前注入新值。注入的值根据基本规则(如果您超过了桶值之间的一半,则进入下一个值)转换为密度桶值(ldpi、mdpi、hdpi等)。
因此,120和160之间的一半点是140,因此133dpi-> ldpi。
2.设备可以随心所欲地做任何事情。对于任何OEM来说,决定其设备桶值的过程都是手动的,并且设置在属性中。它不是根据设备的实际硬件屏幕大小动态计算的。您可以创建一个真实屏幕密度为133的设备,但如果您愿意,将其放在xxdpi存储桶中。
最终结果是,您需要创建一个新的设备定义,手动指定您的7英寸480x800设备实际上是中等密度设备,然后它应该正常工作。如果没有,那么我们为特定基于设备的AVD配置模拟器时存在问题。这不是Android平台本身的问题,因为它不计算任何东西。

当然,我知道OEM定义了自己的值(类似于修改社区,我猜)。关于AVD编辑器,我不确定对话框如何计算密度(没有看到代码)。话虽如此,感谢您通过实验确认了我怀疑的事情:它选择最接近计算密度的桶。有了这些信息,我去找到它确切的位置,并在sdkuilib中的DeviceCreationDialog中找到了SizeListener类,它给出了在那种情况下使用的确切算法。谢谢。 - davidcesarino

2

我已经更新了2014年的另一种解决方案。

在您的活动中调用此方法:

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

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

    }
 }

0

制造商在为您的设备制作ROM映像时选择密度。它不是在运行时计算的。

如果您查看源代码:https://github.com/android/platform_frameworks_base/blob/master/core/java/android/util/DisplayMetrics.java#L294,您将看到getDeviceDensity()函数尝试使用两个系统属性,一个是qemu模拟器值qemu.sf.lcd_density,另一个是制造商设置的值ro.sf.lcd_density,最后,如果制造商忘记设置其中一个,则系统会回退到默认值。DENSITY_DEFAULT设置为DENSITY_MEDIUM,即160

您可以通过插入您的设备并运行此命令来验证设备属性:

adb shell getprop ro.sf.lcd_density

属性被存储在/system/build.prop中,并在启动时加载。您可以使用以下命令查看文件的内容:
adb shell cat /system/build.prop

0
如果您查看文档(查找“使用配置限定符”部分),则设备在达到/低于120 DPI之前被认为是“低DPI”。

是的。Androis说120 = 低;160 = 中等;240 = 高。这很好(我也知道在计算这些标准值时不使用任何舍入)。 - rugo
1
但是为什么安卓将133 DPI四舍五入到160,而不是像我预期的那样四舍五入到120呢? - rugo
我相信他们有充分的理由,但我不知道是什么。 - dmon

0

我正在使用同样的代码,但只看到了160、240、320密度dpi值。我认为这是Android操作系统上的规范化。这只是我的建议,我不知道详细的技术信息。


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