密度无关像素(density-independent pixels)
Android 设备不仅有不同的屏幕尺寸(手机、平板电脑、电视等),而且其屏幕也有不同的像素尺寸。也就是说,有可能一部设备的屏幕为每平方英寸 160 像素,而另一部设备的屏幕在相同的空间内可以容纳 480 像素。如果您不考虑像素密度的这些差异,系统可能会缩放图片(导致图片变模糊),或者图片可能会以完全错误的尺寸显示。
如果开发者只对屏幕分辨率
进行适配,也就是说使用像素(px)
作为度量单位,那么在同一分辨率
下的布局,在不同像素密度
的屏幕中的显示完全不同。为了应对这种情况,Android引入了密度无关像素(dp)
来作为度量单位,但最终的渲染还是使用像素(px)为度量单位,dp是做为一种中间值的存在。
密度无关像素(dp)是一个虚拟像素单位,1 dp 约等于
中密度屏幕
(160dpi;“基准”密度)上的 1 像素。对于其他每个密度,Android 会将此值转换为相应的实际像素数。
Android支持多种多样的屏幕密度
,但是开发者不可能对每种都进行适配,于是将屏幕密度
按一定关系进行划分,开发者只适配同一区间的屏幕密度
成为一种比较好的方案。Android的屏幕密度
分级区间,系统会基于不同区间自行按对应倍数进行缩放(包括布局和图片),开发者也可以基于这些区间规制自行适配,区间如下:
密度限定符 | 说明 | 缩放倍数 |
---|---|---|
ldpi | 适用于低密度 (ldpi) 屏幕 (~ 120dpi) 的资源。(在120dpi左右,而非固定值,不同系统也是有差距的) | 0.75(px=dp*0.75) |
mdpi | 适用于中密度 (mdpi) 屏幕 (~ 160dpi) 的资源(这是基准密度)。默认的layout,dimens也是基于此。 | 1.0(px=dp) |
hdpi | 适用于高密度 (hdpi) 屏幕 (~ 240dpi) 的资源。 | 1.5(px=dp*1.5) |
xhdpi | 适用于加高 (xhdpi) 密度屏幕 (~ 320dpi) 的资源。 | 2.0(px=dp*2) |
xxhdpi | 适用于超超高密度 (xxhdpi) 屏幕 (~ 480dpi) 的资源。 | 3.0(px=dp*3) |
xxxhdpi | 适用于超超超高密度 (xxxhdpi) 屏幕 (~ 640dpi) 的资源。 | 4.0(px=dp*4) |
nodpi | 适用于所有密度的资源。这些是与密度无关的资源。无论当前屏幕的密度是多少,系统都不会缩放以此限定符标记的资源。 | |
tvdpi | 适用于密度介于 mdpi 和 hdpi 之间的屏幕(约 213dpi)的资源。这不属于“主要”密度组。它主要用于电视,而大多数应用都不需要它。对于大多数应用而言,提供 mdpi 和 hdpi 资源便已足够,系统将视情况对其进行缩放。如果您发现有必要提供 tvdpi 资源,应按一个系数来确定其大小,即 1.33*mdpi。例如,如果某张图片在 mdpi 屏幕上的大小为 100px x 100px,那么它在 tvdpi 屏幕上的大小应该为 133px x 133px。 | 1.33(px=dp*1.33) |
例如,你在中密度屏幕上的一个控件或者图片,大小为 48x48 像素,那么它在其他各种密度的屏幕上的大小应该为:
- 36x36 (0.75x) - 低密度 (ldpi)
- 48x48(1.0x 基准)- 中密度 (mdpi)
- 72x72 (1.5x) - 高密度 (hdpi)
- 96x96 (2.0x) - 超高密度 (xhdpi)
- 144x144 (3.0x) - 超超高密度 (xxhdpi)
- 192x192 (4.0x) - 超超超高密度 (xxxhdpi)
如何使用密度无关像素进行适配屏幕
系统适配
- 图片文件48x48(res/drawable/ic_icon.png)
- 布局文件(res/layout/activity_main.xml)
1 |
|
- 资源尺寸文件(res/values/dimens.xml)
1 |
|
系统会根据屏幕密度
分级区间自行按对应倍数进行缩放,在中密度 (mdpi)下text_margin_top
的像素
为15,在超高密度 (xhdpi)下,text_margin_top
的像素
为15x2.0,ic_icon.png
的像素
为96x96。
自定义适配
系统的缩放比在一定条件下,是可以保证应用的显示正常,但是往往不一定那么理想,于是手动去设置尺寸,更为常见,就是能过将对应尺寸的资源放入对应dp文件夹来实现,如:
- 要适配xhdpi,就将同名图片放入
res/drawable-xhdp/ic_icon.png
。 - 要适配xhdpi,就将同名文件放入
res/layout-xhdpi/activity_main.xml
。 - 要适配xhdpi,就将同名文件放入
res/values-xhdpi/dimens.xml
中。
如果使用dp会在相应屏幕密度
分级区间自行按对应倍数进行缩放,也就是说相当于覆盖了默认的res/values/dimens.xml
中的参数,然后进入系统适配(按对应倍数进行缩放),px则不会进行缩放,例如(res/values-xhdpi/dimens.xml):
1 |
|
当前应用如果适配的dpi不匹配设备时,系统会先找比设备dpi更大最近的以适配的dpi,然后是更小最近的,一直到mdpi也就是默认适配文件。例如:
- 当前应用适配有hdpi、xxdpi、xxxdpi,设备的dpi为xhdpi,于是系统给应用分配的是xxdpi中的参数。
- 当前应用适配有hdpi,设备的dpi为xhdpi,于是系统给应用分配的是hdpi中的参数。
- 当前应用适配有ldpi,设备的dpi为xhdpi,于是系统给应用分配的是mdpi(也就是默认值)中的参数。
使用代码强行设置应用显示的屏幕密度
(dpi)
这种方式可以比如只适配了xhdpi,想在其它的dpi中也按xhdpi进行绘制。
1 | private float sNoCompatDensity; |
使用最小宽度限定符(smallestWidth)进行适配
屏幕的基本尺寸,由可用屏幕区域的最小尺寸指定。具体而言,设备的 smallestWidth 是屏幕可用高度和宽度的最小尺寸(您也可将其视为屏幕的“最小可能宽度”)。无论屏幕的当前方向如何,您均可使用此限定符确保应用界面的可用宽度至少为 dp。
例如,如果布局要求屏幕区域的最小尺寸始终至少为 600dp,则可使用此限定符创建布局资源 res/layout-sw600dp/。仅当可用屏幕的最小尺寸至少为 600dp(无论 600dp 表示的边是用户所认为的高度还是宽度)时,系统才会使用这些资源。最小宽度为设备的固定屏幕尺寸特征;即使屏幕方向发生变化,设备的最小宽度仍会保持不变。
该最小宽度的计算方式为:屏幕宽度(px)/屏幕密度缩放倍数
。如你的屏幕为720x1280 xhdpi,那么720/2=360。
使用如下:res/values-sw360dp/dimens.xml
,就是满足最小宽度(dp)为360dpi的屏幕进行适配,这种方式是分辨率+屏幕密度(dpi),算是现在对于多尺寸屏幕适配比较好的方案。
可用宽度(高度)限定符进行适配
您可能希望根据当前可用的宽度或高度来更改布局,而不是根据屏幕的最小宽度来更改布局。例如,如果您有一个双窗格布局,您可能希望在屏幕宽度至少为 600dp 时使用该布局,但屏幕宽度可能会根据设备的屏幕方向是横向还是纵向而发生变化。
使用最小宽度限定符的适配方式对于等比例屏幕效果比较好,如果是屏幕长宽比例较大时就不太适用,这时可以使用可用宽度(高度)符,这两种方案搭配几乎可以满足所有适配需求。
计算方式为与最小宽度限定符一样。
使用如下:res/values-w360dp/dimens.xml
,就是满足宽度(dp)为360dpi的屏幕进行适配或者res/values-h720dp/dimens.xml
就是满足高度(dp)为720dpi的屏幕进行适配。
相关其它
dp与px的转换规则
1 | px = dp * (dpi / 160) |
dpi
:由上表可得,就是每平方英寸可显示的像素数量,如mdpi~240dpi。
设计图中的px与dp转换
在Photoshop中的**像素/英寸 (ppi)**与Android的屏幕密度
(dpi)基本是一个概念,于是在设计图(320ppi)中一个控件的外边距为10px,由上面的转换规则可知dp=px/(dpi/160)
,这里就是10/(320/160)=5,也就是在xhdpi的布局中对应控件的外边距为5dp。
改变设备的屏幕密度
(dpi)
获取设备屏幕分辨率:adb shell wm size
获取设备屏幕密度
(dpi):adb shell wm density
设置屏幕密度
(dpi) :adb shell wm density 320
重置屏幕密度
(dpi) :adb shell wm density reset
注意:虽然改变了系统的参数,但是系统判断自定义适配规制是无效的,会跳过当前dpi,但是最小宽度限定符
是生效的。例如:
- 当前应用适配有hdpi、xxdpi、xxxdpi,给设备设置的是hdpi,于是系统给应用分配的是xxdpi中的参数。
- 当前应用适配有sw480dp,给设备(720p)设置的是hdpi,是正常的。
获取屏幕参数代码片段
1 | public void getAndroiodScreenProperty() { |