安卓屏幕完美适配方案——独家秘笈

5,580 阅读10分钟

这里写图片描述

一、为什么要适配

由于Android系统的开放性,任何用户、开发者、硬件厂商、运营商都可以对Android系统和硬件进行定制,修改成他们想要的样子。

但是这种“碎片化”到达什么程度呢?

这里写图片描述

以上每一个矩形都代表一种机型,且它们屏幕尺寸、屏幕分辨率大相径庭。随着Android设备的增多,设备碎片化、品牌碎片化、系统碎片化、屏幕碎片化的程度也在不断加深。

为了让我们的Android应用在各式各样的手机上运行的时候,能够保持界面效果一直,所以,我们需要对各种手机屏幕进行适配!


二、概念

1、像素(px):

通常所说的像素,就是CCD/CMOS上光电感应元件的数量,一个感光元件经过感光,光电信号转换,A/D转换等步骤以后,在输出的照片上就形成一个点,我们如果把影像放大数倍,会发现这些连续色调其实是由许多色彩相近的小方点所组成,这些小方点就是构成影像的最小单位“像素”(Pixel)。

简而言之,像素就是手机屏幕的最小构成单元


2、分辨率:

手机在横向、纵向上的像素点数总和,一般描述成 宽*高 ,

横向像素点个数*纵向像素点个数


3、屏幕尺寸(in):

手机对角线的物理尺寸,单位 英寸(inch),一英寸大约2.54cm,常见的尺寸有4.7寸、5寸、5.5寸、6寸


4、屏幕像素密度(dpi):

每英寸长度上像素点个数。

例如每英寸内有160个像素点,则其像素密度为160dpi。

公式: 像素密度=像素/尺寸 (dpi=px/in)


5、标准屏幕像素密度(mdpi):

每英寸长度上还有160个像素点,即称为标准屏幕像素密度(mdpi)。


6、像素密度等级:

手机真实像素密度与标准屏幕像素密度(160dpi)的比值。官方给出的0.75、1、1.5、2、3、4,即对应120dpi、160dpi、240dpi、320dpi、480dpi、640dpi。


7、密度无关像素(dp):

density-independent pixel,叫dp或dip,与终端上的实际物理像素点无关。可以保证在不同屏幕像素密度的设备上显示相同的效果,是安卓特有的长度单位。


8、独立比例像素(sp):

scale-independent pixel,叫sp或sip,字体大小专用单位,可根据字体大小首选项进行缩放;

推荐使用12sp、14sp、18sp、22sp作为字体大小,不推荐使用奇数和小数,容易造成精度丢失,12sp以下字体太小


9、尺寸、像素、像素密度关系:

这里写图片描述


10、px与dp关系:

像素=dp*像素密度等级,即px=dp*(dpi/160)


三、对哪些设备适配

注意进行Android设备的屏幕适配操作,不是单单对屏幕尺寸多样的各种设备进行的适配,在诸多的物理尺寸的背后是屏幕的分辨率,现在市面上占比最多的六种分辨率:480*800、320*480、480*854、540*960、720*1280、1080*1920。在日常适配中只要做好对这几个设备的适配,就能很好的适配其他机型。但是在这几种分辨率的背后存在的更为根本的数据是设备的屏幕像素密度。在Google文档中对于屏幕的像素密度进行了几种规定!

另外也需注意对安卓平板、安卓电视等大尺寸、超大尺寸设备的适配。

这里写图片描述
这里写图片描述


四、如何适配

1、适配误区

在进行适配的时候,人们总是关注于:代码、Layout、Dimens、图片、权重,这几种适配方式并不是屏幕适配的全部方案,除此之外还存在多种小细节来实现屏幕适配。

如何理解使用dp为单位进行适配?

这里写图片描述

但是,使用密度无关像素(dp)也不能做到适配所有屏幕!


2、造成误差原因

在长期的Android发展过程中,由于Android设备的增多,Google制定的屏幕密度标准(mdpi、hdpi、ldpi等),在众多厂家的生产过程中,已经被打破,人们没有生产数完全符合屏幕密度标准的Android设备,对于真实手机的屏幕密度值,是在Google标准的周围浮动变化的,但是不乏存在一些厂商生产的设备偏离Google的屏幕密度标准比较大,这个时候再使用dp作为单位就不能完完全全的完成适配操作!(dp只有在大家标准统一的情况下才有更好的发展)

在所有计算公式中存在误差:在计算真实像素密度时运用了开方运算和除法运算,导致所得结果存在误差。

理论计算造成的误差:

在计算对角线上像素点个数时,我们使用勾股定理计算得出,但实则存在误差:

  • 若将像素长度当做1,分辨率指的是横纵向上的1的个数,计算记过表示的是对角线上有多少个1,但理论上对角线上 根号2 的个数才是像素点的个数!
  • 屏幕对角线并不会和像素对角线重合,使计算结果存在误差。
    这里写图片描述

3、ldpi、mdpi、hdpi、xhdpi、xxhdpi的使用

官方截图:
这里写图片描述
这里写图片描述


4、使用wrap_content、match_parent、权重

要确保布局的灵活性并适应各种尺寸的屏幕,应使用 “wrap_content” 、“match_parent”和权重控制某些视图组件的宽度和高度。

使用 “wrap_content”,系统就会将视图的宽度或高度设置成所需的最小尺寸以适应视图中的内容,而 “match_parent”(在低于 API 级别 8 的级别中称为 “fill_parent”)则会展开组件以匹配其父视图的尺寸。

如果使用 “wrap_content” 和 “match_parent” 尺寸值而不是硬编码的尺寸,视图就会相应地仅使用自身所需的空间或展开以填满可用空间。此方法可让布局正确适应各种屏幕尺寸和屏幕方向。


5、使用相对布局,不要使用绝对布局

我们大部分时候使用的都是线性布局、相对布局和帧布局,绝对布局由于适配性极差,所以极少使用。

关于布局的使用应该具体情况具体分析,在进行电视机顶盒的开发中就是使用的是绝对布局。


6、使用限定符进行适配操作

使用尺寸限定符——large

使用最小宽度限定符——swdp

使用屏幕方向限定符


7、多套layout适配

res/values/layouts.xml:

res/values-sw600dp-land/layouts.xml:

res/values-sw600dp-port/layouts.xml:

res/values-large-land/layouts.xml:

res/values-large-port/layouts.xml:


8、使用自动拉伸位图

支持各种屏幕尺寸通常意味着您的图片资源还必须能适应各种尺寸。例如,无论要应用到什么形状的按钮上,按钮背景都必须能适应。

如果在可以更改尺寸的组件上使用了简单的图片,您很快就会发现显示效果多少有些不太理想,因为系统会在运行时平均地拉伸或收缩您的图片。解决方法为使用自动拉伸位图,这是一种格式特殊的 PNG 文件,其中会指明可以拉伸以及不可以拉伸的区域。

.9的制作,实际上就是在原图片上添加1px的边界,然后按照我们的需求,把对应的位置设置成黑色线,系统就会根据我们的实际需求进行拉伸。


9、普通图片处理

稍后会详细介绍。


10、dimens使用

这里写图片描述

如上图,我将市面上各分辨率下的屏幕尺寸,取了平均数,算出对应的真实的屏幕像素密度,与理论要求的屏幕像素密度作了对比,比值在倒数第二列。发现:(干货要来了!)真实像素密度与理论像素密度的比值大致分为两类,取其平均数,一类在1.15左右,另一类则在0.89左右。巧了,它们两类正好各自对应w320dp和w360dp的宽度限定符!所以,dimens只需写两套即可(values-w320dp、values-w360dp),其name与真实数值的比值就是刚刚我们算出的两个平均数! 以后我们就不需要对应各种分辨率写多套dimens了,两套dimens即可。

这里写图片描述


五、图片处理

这里写图片描述

logo需要36*36、48*48、72*72、96*96、144*144、192*192px,图片使用正方形形状,在某些机型上面,会自动显示为圆角正方形;

Android8.0以后,系统增加了logo点击效果和动画,可按以上尺寸制作圆形logo,但图片必须为正方形,圆形以外区域透明。


2、普通图片

这里写图片描述
这里写图片描述

UI切图只需按照720*1280,4.7寸屏幕切图即可;

应为iphone6等分辨率、尺寸、像素密度都与要求接近,可使用IOS的2x图代替。

这里写图片描述


3、纯色图、.9图

纯色按钮或渐变按钮可使用代码设置颜色或.9图实现,不必用图片作为背景。


4、动画、自定义view、shape

可以使用代码进行控制和展示多种视图,如patch动画替代帧动画。


5、ImageView的ScaleType

关于ScaleType请参考这里 点击打开


六、其他

1、代码适配

在代码中使用Google提供的API对设备的屏幕宽度进行测量,然后按照需求进行设置。

几个主要使用的API:

对于当前控件的宽高设置,需要做的操作是首先要获取到该控件的父控件,使用父控件对当前控件的宽高进行设置操作!

API

DisplayMetrics metrics = new DisplayMetrics ();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
手机对应的宽高:
Constants.screenHeight= metrics.heightDixels;
Constants.screenWidth= metrics.widthDixels;
RelativeLayout.LayoutParams=new RelativeLayout.LayoutParams();

(int)( Constants.screenHeight*0.5+0.5f);
(int)( Constants.screenWidth *0.5+0.5f);

在上面的两个计算操作中最后加上0.5f的作用是:进行float强转到int类型的时候会出现都是精度的问题。当使用Java代码进行宽高设置的时候,假如出现320.2dp这样的数据此时直接进行int得到的值是320;但是假如出现320.7这样的数据的时候,由于int的计算规则,会直接强转为320,但是从实际出发,这个时候的值取321更为合适。

所以在计算的最后直接加0.5,这样一来,320.2+0.5=320.7,进行数据的强转操作得到的数据是320,320.7+0.5=321.2,进行数据强转操作得到的数据是321,这样一来得到的数据就和实际预想的更为接近!!


2、接口配合

本地加载图片前判断手机分辨率或像素密度,向服务器请求对应级别图片。


由于时间匆忙和水平有限,文章中不免错误和不足的地方,希望大家予以指出,一起进步!