1、一些概念
屏幕尺寸:指屏幕的对角线的长度,单位是英寸,1英寸=2.54厘米
屏幕分辨率:指在横纵向上的像素点数,单位是px,1px=1个像素点。一般以纵向像素*横向像素,如1960*1080。
屏幕像素密度:指每英寸上的像素点数,单位是dpi,即“dot per inch”的缩写。屏幕像素密度与屏幕尺寸和屏幕分辨率有关,在单一变化条件下,屏幕尺寸越小、分辨率越高,像素密度越大,反之越小。
dp、dip、dpi、sp、px辨析
px:即像素,1px代表屏幕上一个物理的像素点,大多数情况下,比如UI设计、Android原生API都会以px作为统一的计量单位,不过px单位不被建议使用,因为同样100px的图片,在不同手机上显示的实际大小可能不同。偶尔用到px的情况,是需要画1像素表格线或阴影线的时候,用其他单位如dp会显得模糊。
pixels = dips * (density / 160) density有160、240、320 DisplayMetrics metric = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(metric); int width = metric.widthPixels; // 屏幕宽度(像素) int height = metric.heightPixels; // 屏幕高度(像素) float density = metric.density; // 屏幕密度(0.75 / 1.0 / 1.5) int densityDpi = metric.densityDpi; // 屏幕密度DPI(120 / 160 / 240)
dp:这个是最常用但也最难理解的尺寸单位。它与“像素密度”密切相关,假设有一部手机,屏幕的物理尺寸为1.5英寸x2英寸,屏幕分辨率为240x320,则我们可以计算出在这部手机的屏幕上,每英寸包含的像素点的数量为240/1.5=160dpi(横向)或320/2=160dpi(纵向),160dpi就是这部手机的像素密度,像素密度的单位dpi是Dots Per Inch的缩写,即每英寸像素数量。横向和纵向的这个值都是相同的,原因是大部分手机屏幕使用正方形的像素点。
不同的手机/平板可能具有不同的像素密度,例如同为4寸手机,有480x320分辨率的也有800x480分辨率的,前者的像素密度就比较低。Android系统定义了四种像素密度:低(120dpi)、中(160dpi)、高(240dpi)和超高(320dpi),它们对应的dp到px的系数分别为0.75、1、1.5和2,这个系数乘以dp长度就是像素数。例如界面上有一个长度为“80dp”的图片,那么它在240dpi的手机上实际显示为80x1.5=120px,在320dpi的手机上实际显示为80x2=160px。如果你拿这两部手机放在一起对比,会发现这个图片的物理尺寸“差不多”,这就是使用dp作为单位的效果。
dip:与dp完全相同,只是名字不同而已。在早期的Android版本里多使用dip,后来为了与sp统一就建议使用dp这个名字了。
sp:与缩放无关的抽象像素(Scale-independent Pixel)。sp和dp很类似但唯一的区别是,Android系统允许用户自定义文字尺寸大小(在手机settings中可以设置系统字体大小,小、正常、大、超大等),当文字尺寸是“正常”时1sp=1dp=0.00625英寸,而当文字尺寸是“大”或“超大”时,1sp>1dp=0.00625英寸。类似我们在windows里调整字体尺寸以后的效果——窗口大小不变,只有文字大小改变。对于使用sp作为字体大小单位的app,如果不需要根据系统字大小的改变影响应用内字体大小,可以通过在自定义的所有Activity的基类BaseActivity中添加如下代码:
@Override public Resources getResources() { Resources res = super.getResources(); Configuration config=new Configuration(); config.setToDefaults(); res.updateConfiguration(config,res.getDisplayMetrics() ); return res; }
/** * dp、sp 转换为 px 的工具类 * */ public class DisplayUtil { /** * 将px值转换为dip或dp值,保证尺寸大小不变 * * @param pxValue * @param scale * (DisplayMetrics类中属性density) * @return */ public static int px2dip(Context context, float pxValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (pxValue / scale + 0.5f); } /** * 将dip或dp值转换为px值,保证尺寸大小不变 * * @param dipValue * @param scale * (DisplayMetrics类中属性density) * @return */ public static int dip2px(Context context, float dipValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dipValue * scale + 0.5f); } /** * 将px值转换为sp值,保证文字大小不变 * * @param pxValue * @param fontScale * (DisplayMetrics类中属性scaledDensity) * @return */ public static int px2sp(Context context, float pxValue) { final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; return (int) (pxValue / fontScale + 0.5f); } /** * 将sp值转换为px值,保证文字大小不变 * * @param spValue * @param fontScale * (DisplayMetrics类中属性scaledDensity) * @return */ public static int sp2px(Context context, float spValue) { final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; return (int) (spValue * fontScale + 0.5f); } }
mdpi、hdpi、xdpi、xxdpi
其实之前还有个ldpi,但是随着移动设备配置的不断升级,这个像素密度的设备已经很罕见了,所在现在适配时不需考虑。
mdpi、hdpi、xdpi、xxdpi用来修饰Android中的drawable文件夹及values文件夹,用来区分不同像素密度下的图片和dimen值。
那么如何区分呢?Google官方指定按照下列标准进行区分:
名称 像素密度范围
mdpi 120dpi~160dpi
hdpi 160dpi~240dpi
xhdpi 240dpi~320dpi
xxhdpi 320dpi~480dpi
xxxhdpi 480dpi~640dpi
2、提供可选资源
- 语言和地区
android系统采用的是ISO 639-1国际语言码,有两个字母组成。地区码采用的是ISO 3166-1-alpha-2,也是两个字母。其中地区码是可选的,如果加上的话,需要在前面加上“r”。例如:en(表示英语),en-rUS(表示英语和美国地区)。在程序中可以通过Configuration类的locale属性值来获取当前设备的语言地区信息。
- 最小宽度(Smallest Width)
格式是sw<N>dp
- 可用宽度(Available Width)
格式是w<N>dp
- 可用高度(Available Height)
格式是h<N>dp
这个后缀的使用方式和w<N>dp一样,随着屏幕横纵向的变化,屏幕高度也会变化,根据变化后的高度值来判断是否使用h<N>dp ,但这种方式很少使用,因为屏幕在纵向上通常能够滚动导致长度变化,不像宽度那样基本固定,因为这个方法灵活性不是很好,google官方文档建议尽量少使用这种方式。
- 屏幕大小(Screen Size)
在Android系统中,将屏幕尺寸大致分为以下几类:
- 屏幕的宽高外观(Screen Aspect)
这个属性是指当前屏幕的宽高比(aspect ratio)有两种结果:
- 屏幕方向(Screen Orientation)
分为两种,即:竖屏(port)、横屏(land)
- UI模式(UI Mode)
UI模式分为以下几种:car、desk、television、appliance
- 夜间模式(Night Mode)
分为两种nigth、notnight。可以通过UiModeManager来开启或关闭这一功能。
- 屏幕像素密度(dpi)
- 平台版本(Platform Version)
设备所支持的API等级值
3、屏幕适配要点
- 为不同的屏幕尺寸提供多种layout布局
建议对尺寸跨度大的屏幕,应用程序应该提供多种layout布局
- 为不同的屏幕方向提供多种layout布局
- 为不同的屏幕密度提供相应的图片资源
- 显式声明应用程序支持的屏幕类型
- 布局时注意事项
使用wrap_content、match_parent、weight
要确保布局的灵活性并适应各种尺寸的屏幕,应使用 “wrap_content” 和 “match_parent” 控制某些视图组件的宽度和高度。
weight是线性布局的一个独特的属性,我们可以使用这个属性来按照比例对界面进行分配,完成一些特殊的需求。android:layout_weight的真实含义是:如果View设置了该属性并且有效,那么该 View的宽度等于原有宽度(android:layout_width)加上剩余空间的占比。
使用自动拉伸位图
关于高清设计图尺寸
Google官方给出的高清设计图尺寸有两种方案,一种是以mdpi设计,然后对应放大得到更高分辨率的图片,另外一种则是以高分辨率作为设计大小,然后按照倍数对应缩小到小分辨率的图片。
根据经验,我更推荐第二种方法,因为小分辨率在生成高分辨率图片的时候,会出现像素丢失,我不知道是不是有方法可以阻止这种情况发生。
而分辨率可以以1280*720或者是1960*1080作为主要分辨率进行设计。
ImageView的ScaleType属性
设置不同的ScaleType会得到不同的显示效果,一般情况下,设置为centerCrop能获得较好的适配效果。
动态设置
有一些情况下,我们需要动态的设置控件大小或者是位置,比如说popwindow的显示位置和偏移量等,这个时候我们可以动态的获取当前的屏幕属性,然后设置合适的数值
public class ScreenSizeUtil { public static int getScreenWidth(Activity activity) { return activity.getWindowManager().getDefaultDisplay().getWidth(); } public static int getScreenHeight(Activity activity) { return activity.getWindowManager().getDefaultDisplay().getHeight(); } }