一. 名词解释
o 像素(Pixel, PX)
表示屏幕上一个物理的像素点。
o 屏幕尺寸
屏幕的物理尺寸,一般用屏幕对角线长度来表示,单位英寸(inch)。1in
=2.54cm。通常我们说手机5寸屏,指的就是手机屏幕的对角线长度是5寸。
o 屏幕分辨率(Screen Resolution)
屏幕垂直方向像素数目px *
屏幕水平方向像素数目px,如1280×800,表示屏幕横向有800个像素点,纵向有1280个像素点。相同尺寸屏幕下屏幕分辨率越高显示效果越精细细腻。
o 像素密度(PPI, Pixels Per Inch)
表示每inch上可以显示的像素点数目,单位px。 与屏幕尺寸、分辨率的关系如下:
o 屏幕密度(DPI,Dots Per Inch)
表示每Inch上可以显示的(打印)点数,单位dpi。如160dpi。在打印机设备上常用。PPI和DPI的详细区别请看https://99designs.com/blog/tips/ppi-vs-dpi-whats-the-difference/。Android提供了DisplayMetrics类和Configuration类获取当前屏幕密度:
context.getResources().getDisplayMetrics().densityDpi
context.getResources().getConfiguration().smallestScreenWidthDp
o 密度无关像素(DP/DIP,Density Independent Pixels)
Android特有的长度单位,可以理解为与具体设备无关的虚拟像素。在特定设备上显示时系统会自动根据屏幕密度确定最终对应的物理长度(px单位)。所以用dp定义的UI可以自适应各种屏幕。Android规定在屏幕密度160dpi的设备上1dp
= 1px。所以虚拟像素dp和物理像素px的换算关系是:
具体函数实现如下,其中density=DPI/160。
public static intdp2px(Context context, floatdpValue){
floatscale=context.getResources().getDisplayMetrics().density;return(int)(dpValue * scale + 0.5f);}
public static intpx2dp(Context context, floatpxValue){
floatscale=context.getResources().getDisplayMetrics().density;return(int)(pxValue / scale + 0.5f);}
o 比例无关像素(SP, Scale Independent Pixels)
Android特有的字体大小单位,可以理解为与具体字体缩放比例无关的虚拟字体大小。在特定设备上显示时系统会自动根据字体缩放比例确定最终字体大小。所以用sp定义的字体大小可以自适应各种大小的系统字体设置。Android规定在屏幕密度160dpi的设备上缩放比例为100%时1sp
= 1px, 所以sp和物理像素px的换算关系是:
具体函数实现如下,其中scaledDensity=scale * (DPI/160)。
public static intsp2px(Context context, floatspValue){
floatfontScale = context.getResources().getDisplayMetrics().scaledDensity;return(int) (spValue * fontScale + 0.5f);}
public static intpx2sp(Context context,floatpxValue){
floatfontScale = context.getResources().getDisplayMetrics().scaledDensity;return(int) (pxValue / fontScale + 0.5f);}
所以使用sp定义的字体是根据系统字体设置而变化的。如果不希望UI的字体大小随系统设置变化,可以用dp定义字体大小。
二. 视觉大小一致性
Android为了使一套UI自动适应不同分辨率的屏幕,实现“与设备密度无关的视觉大小一致性”,也就是说无论是在高分辨率手机、还是在低分辨率的平板上,某个控件或者图片在物理尺寸上都是一样的,这样UI就不会因为设备的不同而显示得千奇百怪,才不会让用户觉得别扭。
所以发明了dp并定义了dp与物理像素的对应关系,同时把手机屏幕根据屏幕密度分成5个级别:
根据上表,为了让APP的UI或者说图片在不同屏幕密度上保持视觉大小一致,也就是在不同屏幕上占有相同的dp,
那么应该针对不同屏幕提供特定比例的布局或图片, 比如mdpi下某个icon尺寸是48X48,
那么这个icon在ldpi下应该为36X36, hdpi为72X72, xhdpi为96X96,
xxhpdi为144X144,xxxhdpi为192X192。
三.
Android适配规则
o UI元素的大小尽量以dp为单位或直接指定为 wrap_content。
o LinearLayout中尽量使用weight分配大小。
o
o
o 图片国际化,
o 如果需要为不同屏幕单独写不同的layout,
四.
Android资源加载规则
o 图片加载
xhdpi->xxhdpi->xxxhdpi(如果没有更高的了)->nodpi(如果有的话)->hdpi->mdpi,如果在xxhdpi中找到目标图片,则压缩2/3来使用,如果在mdpi中找到图片,则放大2倍来使用。
o values加载
也就是说,如果我们同样在values-xhdpi和values下写 length=10dp
那么在mdpi设备上得到的都是10px,在xhdpi设备上得到的都是20px。
但它们看起来“都是一样宽”,这样就已经是“保证视觉大小一致性了”。
48dp法则告诉我们,48dp的物理尺寸约等于9mm,是人的手指比较容易点击到的大小,并且是于设备的。
考虑这样一个情况:有一个BottomBar,左右两端各有一个按钮,按钮长宽用dp定义,这样在一个大屏手机中,两个按钮可能就相隔更远了,因为按钮的视觉尺寸是没有变化的。如果你想保持按钮在BottomBar中所占的比例,最好办法不是添加一个values-xxx,然后重写这两个dp值,而是精心设计你的布局。
values-xhdpi-1184x800->values-xhdpi-1184x720->values-xhdpi
-> hdpi-1280x720 -> hdpi -> …)
也就是说,对于同dpi的多台不同分辨率平板设备,如果布局足够通用,我们可以只针对最低分辨率设计dimens即可,上面的例子中,则是values-xhdpi-1184x720。
我们还可以将这个分辨率写得更低,低到我们有把握:如果再出现比这个分辨率更低的设备,那么它的物理尺寸一定满足即使采用values-xhdpi中针对手机物理尺寸设计的大小也没有问题。
再举一个复杂的例子
=============
在 720x1280 xhdpi 的机器上,搜寻顺序是:
values-xhdpi-720x1280(1080x1920 不会访问)
values-xhdpi-480x800
values-xhdpi-320x480(到底)
values-xh
values-xxh
values-xxxh(到顶)
values-h
values-m
values-720x1280(1080x1920 不会访问)
values-480x800
values-320x480(到底)
values
values-l
如果还没找到则报错
====================================