将屏幕密度放入正确的桶中

时间:2015-11-16 16:19:38

标签: android

一组六个广义密度:

ldpi (low) ~120dpi
mdpi (medium) ~160dpi
hdpi (high) ~240dpi
xhdpi (extra-high) ~320dpi
xxhdpi (extra-extra-high) ~480dpi
xxxhdpi (extra-extra-extra-high) ~640dpi

从每个手机显示屏的维基页面

缩小到桶中,即如果ppi是300,那么它会进入hdpi桶,因为它少于320?

Galaxy S3 306ppi -> bucket hdpi
Nexus 4  318ppi -> bucket hdpi
Nexus 5  445ppi -> bucket xhdpi
Nexus 5X 432ppi -> bucket xhdpi
Nexus 6  493ppi -> bucket xxhdpi 
Nexus 6P 518ppi -> bucket xxhdpi

这是解决屏幕尺寸的正确方法。

我问的原因是因为我创建了以下值目录资源:

values-hdpi/dimens
values-xhdpi/dimens
values-xxhdpi/dimens
values-xxxhdpi/dimens

dimens.xml我有不同的边距,并根据铲斗大小设置dp,即

<dimen name="network_quantity_margin_top">100dp</dimen>

我很想知道这是否是正确的方法。

6 个答案:

答案 0 :(得分:41)

  

我问的原因是因为我创建了以下值目录资源。 (......)   在dimens.xml中,我有不同的边距,并根据桶大小设置dp。 (......)   我很想知道这是否是正确的方法。

我不确定为什么要根据密度在dp中指定不同的边距。对于基线密度,将边距指定为dp一次,已经为您处理了所有其他密度,这意味着在任何设备上显示边距的物理尺寸都是相同的。

如果您使用px代替dp(但不是),那么您必须自己为不同的屏幕进行缩放。

  

缩小到桶中,即如果ppi是300,那么它会进入hdpi桶,因为它少于320?

是的,但不是因为它小于320.如果有经验法则我会说它是四舍五入到最近的广义密度。请参阅此插图,了解Android如何将实际密度粗略地映射到广义密度(图不准确):

generalized densities

the documentation的相关部分是:

  

每个通用尺寸和密度涵盖一系列实际屏幕尺寸和密度。例如,两个报告屏幕大小正常的设备可能具有实际屏幕尺寸和纵横比,当手动测量时,这些尺寸和纵横比略有不同。同样,报告屏幕密度 hdpi 的两个设备可能具有略微不同的实际像素密度。 Android将这些差异抽象为应用程序,因此您可以提供针对通用大小和密度设计的UI,并让系统根据需要处理任何最终调整。

再说一遍,如果您只是编写应用程序,那么您应该不关心如何 Android这样做。你应该关心的是:

  • 根据需要在dpwrap_content / match_parent中指定所有layout dimension值(文本可以在sp中以额外匹配用户首选项,但除了文字之外别无其他),
  • 根据屏幕的physical sizeorientation考虑​​不同的布局,
  • 为不同的densities提供位图资源,只是为了避免模糊或像素化的工件(因为如果您使用dpwrap_content,Android会将它们缩放到具有正确的物理尺寸。)< / LI>
根据正在使用的屏幕的实际密度

Android将lookup the best matching resourcethen transparently handle根据需要对dp单位进行任何缩放。将dp单位转换为屏幕像素非常简单:px = dp * (dpi / 160)

注意实际密度而不是广义密度。后者为开发人员带来便利因为不可能为每个屏幕提供可绘制的内容。这样开发人员只需要提供3或4组图形,而Android会选择最接近的版本,并根据特定设备的需要进一步调整。 (现在可以使用一个矢量绘图而不是许多预先缩放的光栅图形,这意味着更好的质量和更小的尺寸。)

  

这是解决屏幕尺寸的正确方法。

不,不是。根据{{​​3}},您列出的所有设备都会比您预期的要高:

Galaxy S3    NA        NA
Nexus 4     318     xhdpi
Nexus 5X    424    xxhdpi
Nexus 5     445    xxhdpi
Nexus 6     493   xxxhdpi
Nexus 6P    515   xxxhdpi

我从该列表中选取了其他一些设备,并根据实际物理密度绘制了不同设备如何落入密度桶中。

Google device metrics

Chromebox 30            101      mdpi
Chromebook 11           135      mdpi
Samsung Galaxy Tab 10   149      mdpi
Nexus 7 '12             216     tvdpi
Android One             218      hdpi
Chromebook Pixel        239     xhdpi
Nexus 9                 288     xhdpi
Nexus 10                299     xhdpi
Moto X                  312     xhdpi
Nexus 4                 318     xhdpi
Nexus 7 '13             323     xhdpi 
Moto G                  326     xhdpi
Dell Venue 8            359     xhdpi
LG G2                   424    xxhdpi
Nexus 5X                424    xxhdpi
HTC One M8              441    xxhdpi
Nexus 5                 445    xxhdpi
Nexus 6                 493   xxxhdpi
Nexus 6P                515   xxxhdpi
LG G3                   534    xxhdpi

你可以看到,除了一些值得注意的例外情况,选择最接近的广义密度的规则成立。

Nexus 6和6P的例外情况被列为xxxhdpi,即使LG G3具有更高的物理密度并且仍远离640px / in。 Android One为hdpi,但仅比Nexus 7 '12 tvdpi稍微密集一些。 Chromebox 30和Chromebook Pixel(不可否认,不是Android)已分配给存储分区mdpixhdpi,即使它们分别低于ldpihdpi

答案 1 :(得分:10)

  

我很想知道这是否是正确的方法。

主要是正确。

问题出在这一部分:

我问的原因是因为我创建了以下值目录资源:

values-hdpi/dimens
values-xhdpi/dimens
values-xxhdpi/dimens
values-xxxhdpi/dimens

在dimens.xml中,我有不同的边距,并根据桶大小设置dp,即

<dimen name="network_quantity_margin_top">100dp</dimen>

dp的目的通过定义values-hdpi/dimens等文件夹而失败。 密度像素(按设计,与设备无关 - 100dp dpi = 240 的设备上看起来像一样宽/长 dpi = 480 的设备上。因此,如果您希望自己的应用看起来一致,请不要为不同的屏幕密度提供不同的尺寸。

考虑这一点的正确方法是认识到受不同屏幕密度影响的唯一资源是drawable。与密度= 480的屏幕相比,dpi = 240的屏幕上的drawable两倍。我确信您提供的文件夹为drawable-hdpidrawable-xhdpi等来处理这个问题。对于其他所有内容,尤其是维度,请使用dp。对于文字大小,请使用scaled-pixels - sp

更重要的是,您应该担心可用于Android的不同屏幕尺寸的范围。与5英寸手机相比,你如何使用10英寸设备上的所有额外屏幕空间?您应该更关注-normal-largexlarge等限定符。

总结:

  • 考虑某个屏幕大小相同的所有设备 - 使用density pixels时,它们的屏幕密度无关紧要。
  • 对于您使用的每个drawable资源,将其缩放版本放在您希望支持的存储区中。请记住,如果您没有为某个存储桶提供资源(比如说drawable-hdpi),那么android会缩小您的drawables drawable-xhdpi文件夹(如果定义了drawable-xhdpi)。反之亦然:如果您已将所有可绘制的内容放在drawable-xhdpi中,则android会在xxhdpi设备上向上扩展您的drawable。结果将是模糊的图形 - 因为扩大规模。

我知道这里有一点陡峭的坡度:)。所以,如果你需要澄清一些,请给我留言。

答案 2 :(得分:6)

来自Android文档:

Supporting Multiple Screens

  

在某些情况下,您需要以dp表示尺寸,然后再表示   将它们转换为像素。想象一下滚动或滚动的应用程序   用户的手指移动后识别出手势   至少16像素。在基线屏幕上,用户必须移动16个像素   / 160 dpi,相当于之前的1/10英寸(或2.5 mm)   手势得到认可。在具有高密度显示器的设备上   (240dpi),用户必须移动16像素/ 240 dpi,等于   1/15英寸(或1.7毫米)。距离要短得多   因此,应用对用户来说似乎更敏感。

     

要解决此问题,必须在代码中表示手势阈值   dp然后转换为实际像素。例如:

// The gesture threshold expressed in dp
private static final float GESTURE_THRESHOLD_DP = 16.0f;

// Get the screen's density scale
final float scale = getResources().getDisplayMetrics().density;
// Convert the dps to pixels, based on density scale
mGestureThreshold = (int) (GESTURE_THRESHOLD_DP * scale + 0.5f);

// Use mGestureThreshold as a distance in pixels...
  

DisplayMetrics.density字段指定您必须的比例因子   用于根据当前屏幕将dp单位转换为像素   密度。在中密度屏幕上,DisplayMetrics.density等于   1.0;在高密度屏幕上,它等于1.5;在超高密度屏幕上,它等于2.0;在低密度屏幕上,它等于0.75。   此数字是您应该将dp单位乘以的因子   为了获得当前屏幕的实际像素数。 (然后加   当转换为整数时,0.5f将图形向上舍入到最接近的整数。)有关更多信息,请参阅   DisplayMetrics上课。

答案 3 :(得分:2)

你必须以dp而不是dpi(每英寸点数)来处理有关屏幕宽度的资源

例如nexus 5 1920 X 1080 480 dpi它使用xxhdpi 和nexus 6p 2560 X 1440 560 dpi它使用的是xxhdpi而不是xxxhdpi !!

处理它使用最宽的资源&#34; drawable-swXXXdp&#34;

dp中的宽度= pixle中的宽度/(dpi / 160) dp = 1440 /(560/160)= ~411

create drawable-sw411dp 

Reference

答案 4 :(得分:1)

在谈到Android设备时,PPI == DPI

PPI(每英寸像素)和DPI(每英寸点数)意味着同样的事情,因为它们是密度的屏幕;因此,您对PPI与DPI的关系是正确的。

可以找到详细解释here。本文的关键部分如下:

  

屏幕密度被引用为每英寸像素数,PPI,并且是   适合一英寸的像素数。数字越高那么   更清晰的图像在显示屏上显示,因此消费者会考虑使用   高PPI在购买设备时具有优势。有时候   图被引用为 Dots Per Inch ,DPI ......

答案 5 :(得分:0)

有点晚,但这对其他读者可能有用。屏幕密度和Android用于从中选择资源的“存储桶”可能会造成混淆。 Google的文档非常密集,在编写代码时需要大量工作才能将其精简为有用的东西。部分原因是有几个因素,它们告诉您有关屏幕密度和倾斜度的所有信息。但是简短的答案是这样。

基本上,dpi是您的定义因素,(如果您依赖其他因素,例如小/中/大),这不是您的答案。否则,我发现this的答案非常有用且简单。这是我从应用启动时运行的各种来源收集的一些代码,用于确定显示信息。

屏幕密度告诉我设备支持的dpi级别。

float density = context.getResources().getDisplayMetrics().density;

接下来,我有一个简单的设备指标方法来告诉我有关屏幕的信息。 (请注意,我正在使用Timber logger)。

protected static void checkDeviceSize(AppCompatActivity context) {
    DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
    Display display = context.getWindowManager().getDefaultDisplay();
    DisplayMetrics outMetrics = new DisplayMetrics();
    display.getMetrics(outMetrics);

    float density = context.getResources().getDisplayMetrics().density;
    float dpHeight = outMetrics.heightPixels / density;
    float dpWidth = outMetrics.widthPixels / density;
    String dpiName = getDpiName(density);
    Timber.e("density  :" + density + " [" + dpiName + "]");
    Timber.e("height dp:" + dpHeight + ", (" +outMetrics.heightPixels + "px)");
    Timber.e("width dp :" + dpWidth + ", (" + outMetrics.widthPixels + "px)");

}

我也有这个简单的帮助程序方法,该方法确定DPI名称以支持上述方法。

public static final String DPI_LDPI = "ldpi";
public static final String DPI_MDPI = "mdpi";
public static final String DPI_HDPI = "hdpi";
public static final String DPI_XHDPI = "xhdpi";
public static final String DPI_XXHDPI = "xxhdpi";
public static final String DPI_XXXHDPI = "xxxhdpi";
public static final String DPI_TVDPI = "tvdpi";

private static String getDpiName(float density) {
    String result = "undefined";
    if (density < 1.0) {
        result = DPI_LDPI;
    } else if (density == 1.0f) {
        result = DPI_MDPI;
    } else if (density <= 1.3f) {
        result = DPI_TVDPI;
    } else if (density <= 1.5f) {
        result = DPI_HDPI;
    } else if (density <= 2.0f) {
        result = DPI_XHDPI;
    } else if (density <= 3.0f) {
        result = DPI_XXHDPI;
    } else if (density <= 4.0f) {
        result = DPI_XXXHDPI;
    }
    return result;
}

最后this video仍然与今天有关。