Android UI之屏幕适配
Android屏幕适配是开发者不得不面临的问题,作为app开发者,你需要考虑应用的兼容性,作为整机开发者,你面临着在一整套的代码中对你当前开发的机型做出适配,解决UI界面显示的问题。
为什么Android需要屏幕适配?
由于Android系统的开放性,任何用户、开发者、OEM厂商、运营商都可以对Android进行定制,修改成他们想要的样子。于是就产生了各种形状,各种尺寸,各种分辨率的Android手机。
据统计数据表明
2012年,支持Android的设备共有3997种。
2013年,支持Android的设备共有11868种。
2014年,支持Android的设备共有18796种。
而随着支持Android系统的设备(手机、平板、电视、手表)的增多,设备碎片化、品牌碎片化、系统碎片化、传感器碎片化和屏幕碎片化的程度愈演愈烈。
网络上有一组对比图片可以形象的提现Android屏幕碎片化的严重程度。
图 1
图 2
图1是Android屏幕尺寸的示意图,蓝色矩形的大小代表不同尺寸,颜色深浅则代表所占百分比的大小,图2则对应的是IOS设备。
不光是屏幕尺寸,再配上不同的屏幕分辨率,PPI,导致我们需要适配的产品是几何级的。
如此之多的屏幕尺寸,为了让我们开发的程序能够比较美观的显示在不同尺寸、分辨率、像素密度的设备上,那就要在开发的过程中进行处理。由于最终将显示在每个像素点上,我们具体的工作可以从单纯的尺寸大小转换到像素大小和像素密度的角度来。
研究屏幕适配不得不提的重要概念
屏幕尺寸:屏幕的对角线的长度,单位是英寸,1英寸=2.54厘米,比如说一款5.5寸的新机,对角线长度约为14cm
屏幕分辨率:屏幕两个方向上的像素点数,1px=1个像素点,
屏幕像素密度PPI:每英寸上的像素点数,单位是dpi,即“dot per inch”的缩写,也就是说,像素密度是尺寸和分辨率共同决定的
计算公式:举例 1920*1080,5.5寸--- (√X+Y)/屏幕尺寸 = 401dpi(300以上看不到颗粒感)
px:像素点,目前大多数的UI设计和代码实现都使用px作为单位
Dpi:像素密度的单位,每英寸上的像素点数
Dip,dp:密度无关像素Density Independent Pixels,dp和px如何换算呢?在Android中,规定以160dpi为基准,1dp=1px,如果密度是320dpi,则1dp=2px。这样做就相当于减少了变量,这个尺寸中包含了屏幕密度的概念。比如说160px,在480*800分辨率手机上显示为1/3屏幕宽度,在320*480的分辨率上则显示为1/2屏幕宽度。而160dp则不同,在两个屏幕上显示的都是统一的。
Px转dp(向前进一位):
public static int px2dp(Context context, float pxValue) {
//与160比较之后的比例
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
dp转px(向前进一位):
public static int dip2px(Context context,float dipValue){
final float scale=context.getResources().getDisplayMetrics().density;
return(int)(dipValue*scale+0.5f);
}
Sp:scale-independent pixels,与文字的缩放比例(于屏幕密度,字体的纵横比等有关)无关,减少变量,增加适配能力。是字体的统一单位,由微软,苹果等公司统一的单位
将px值转换为sp值,
public static int px2sp(Context context, float pxValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (pxValue / fontScale + 0.5f);
}
将sp值转换为px值
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用来修饰Android中的drawable文件夹及values文件夹,用来区分不同像素密度下的图片和dimen值,在进行开发的时候,我们需要把合适大小的图片放在合适的文件夹里面。
名称 | 像素密度范围 |
mdpi | 120-160dpi |
hdpi | 160-240dpi |
xdpi | 240-320dpi |
xxdpi | 320-480dpi |
xxxdpi | 480-640dpi |
对于图标设计者来说,这五种主流的像素密度范围的设计应该满足2:3:4:6:8:10,ldpi系统会自动转换为1/2 hdpi
如何支持各种屏幕设备
1.使用wrap_content、match_parent、weight
wrap_content:设置成所需的最小尺寸
match_parent:填充父布局
Weight:线性布局的特性,按比例适配
2.使用相对布局,禁用绝对布局
3.使用限定符
尺寸限定符:
res/layout/main.xml,单面板(默认)布局
res/layout-large/main.xml,双面板布局
文件夹上的large限定符,系统会在7寸以上选择双面板布局,七寸以下使用单面板
最小宽度限定符:
res/layout/main.xml
res/layout-sw600dp/main.xml
res/values-sw600dp-port/layouts.xml
文件夹加上sw600dp限定符,表示该文件夹下的资源适用与最小宽度大于600dp的设备(比如说修改一个bug,你在360hdpi上修改有效,那么你有可能会影响所用最小宽度小于360dp的设备,需要在360xdpi上面也进行对应的修改,把你要改的内容卡在中间)
使用屏幕方向限定符:
res/values-sw600dp-land/layouts.xml
4.使用图片使用自动拉伸位图
如果只是简单的进行屏幕拉伸,会带来模糊等负面效果,我们使用nine patch位图资源,自动拉伸位图,这是一种格式特殊的 PNG 文件,会指明可拉伸和不可拉伸的部位,比如说中间的区域不拉伸,四周的背景进行拉伸,就不会出现拉伸后变形的效果。
5.使用非密度制约像素dp和sp
但是这样也并不是万无一失去,因为屏幕的宽度千变万化,我们使用最小宽度限定符sw600dp加上屏幕密度hdpi可以限定绝大多数的机型,但还是可能有细微的差异,需要我们进行适配和规避
Android overlay 机制
允许在不修改packages中apk的情况下,来自定义 framework和package中的资源文件,实现资源的定制。来达到显示不同的UI得目的。
有几类资源可以通过Overlay配置
1.配置文件:string, bool, bool-array
2.本地化:string, string-array
3.UI外观:color, drawable, layout, style, theme, animation
4.资源文件:audio, video, xml
Overlay的步骤:
1.mk文件为产品添加Overlay目录
PRODUCT_PACKAGE_OVERLAYS和DEVICE_PACKAGE_OVERLAYS,两个功能一样但是优先级不一样,如果同时配置了二者,PRODUCT_PACKAGE_OVERLAYS的优先级更高,最后起作用的是它。需要在mk文件下进行如下添加
PRODUCT_PACKAGE_OVERLAYS := device/vendor-name/device-name/product-name/overlay $(PRODUCT_PACKAGE_OVERLAYS)
2.在overlay目录下添加资源文件
想覆盖Android系统自带package中资源文件, 那么在overlay目录下必须包含和要替换package相同的路径
packages/apps/Settings/res/
./overlay/packages/apps/Settings/res/