本文为自身的总结与结合其余文章引用而成,分别为:android
wangwangli6:Android开发:最全面、最易懂的Android屏幕适配解决方案程序员
jiashuai94:安卓屏幕完美适配方案——独家秘笈面试
司小三石:android 屏幕适配的总结,适合面试windows
宇宝守护神:ImageView的scaleType的属性理解bash
秦子帅:Android刘海屏适配方案
(暂未总结刘海屏适配,若有须要请查看这篇文章)服务器
自身的思考&实践app
因为Android系统的开放性,任何用户、开发者、硬件厂商、运营商均可以对Android系统和硬件进行定制,修改为他们想要的样子。 那么这种“碎片化”到达什么程度呢? ide
以上每个矩形都表明一种机型,且它们屏幕尺寸、屏幕分辨率截然不同。随着Android设备的增多,设备碎片化、系统碎片化、屏幕尺寸碎片化、屏幕碎片化的程度也在不断加深。
备注:布局
当Android系统、屏幕尺寸、屏幕密度出现碎片化的时候,就很容易出现同一元素在不一样手机上显示不一样的问题。试想一下这么一个场景: 为4.3寸屏幕准备的UI设计图,运行在5.0寸的屏幕上,极可能在右侧和下侧存在大量的空白;而5.0寸的UI设计图运行到4.3寸的设备上,极可能显示不下。
为了保证用户得到一致的用户体验效果,使得某一元素在Android不一样尺寸、不一样分辨率的、不一样系统的手机上具有相同的显示效果,可以保持界面上的效果一致,咱们须要对各类手机屏幕进行适配!性能
密度类型 | 表明的分辨率(px) | 屏幕像素密度(dpi) |
---|---|---|
低密度(ldpi) | 240 x 320 | 120 |
中密度(mdpi) | 320 x 480 | 160 |
高密度(hdpi) | 480 x 800 | 240 |
超高密度(xhdpi) | 720 x 1280 | 320 |
超超高密度(xxhdpi) | 1080 x 1920 | 480 |
一部手机的分辨率是宽x高,屏幕大小是以寸为单位,那么三者的关系是:
假设一部手机的分辨率是1080x1920(px),屏幕大小是5寸
密度类型 | 表明的分辨率(px) | 屏幕密度(dpi) | 换算 |
---|---|---|---|
低密度(ldpi) | 240 x 320 | 120 | 1dp = 0.75px |
中密度(mdpi) | 320 x 480 | 160 | 1dp=1px |
高密度(hdpi) | 480 x 800 | 240 | 1dp=1.5px |
超高密度(xhdpi) | 720 x 1280 | 320 | 1dp=2px |
超超高密度(xxhdpi) | 1080 x 1920 | 480 | 1dp=3px |
追到android源码,发现系统内部用applyDimension() (路径:android.util.TypedValue.applyDimension())将全部单位都转换成px 再处理:
/**
* Converts an unpacked complex data value holding a dimension to its final floating
* point value. The two parameters <var>unit</var> and <var>value</var>
* are as in {@link #TYPE_DIMENSION}.
*
* @param unit The unit to convert from.
* @param value The value to apply the unit to.
* @param metrics Current display metrics to use in the conversion --
* supplies display density and scaling information.
*
* @return The complex floating point value multiplied by the appropriate
* metrics depending on its unit.
*/
public static float applyDimension(int unit, float value,
DisplayMetrics metrics)
{
switch (unit) {
case COMPLEX_UNIT_PX:
return value;
case COMPLEX_UNIT_DIP:
return value * metrics.density;
case COMPLEX_UNIT_SP:
return value * metrics.scaledDensity;
case COMPLEX_UNIT_PT:
return value * metrics.xdpi * (1.0f/72);
case COMPLEX_UNIT_IN:
return value * metrics.xdpi;
case COMPLEX_UNIT_MM:
return value * metrics.xdpi * (1.0f/25.4f);
}
return 0;
}
能够发现dp和sp的区别在于density和scaledDensity两个值上;
/**
* The logical density of the display. This is a scaling factor for the
* Density Independent Pixel unit, where one DIP is one pixel on an
* approximately 160 dpi screen (for example a 240x320, 1.5"x2" screen),
* providing the baseline of the system's display. Thus on a 160dpi screen * this density value will be 1; on a 120 dpi screen it would be .75; etc. * * <p>This value does not exactly follow the real screen size (as given by * {@link #xdpi} and {@link #ydpi}, but rather is used to scale the size of * the overall UI in steps based on gross changes in the display dpi. For * example, a 240x320 screen will have a density of 1 even if its width is * 1.8", 1.3", etc. However, if the screen resolution is increased to * 320x480 but the screen size remained 1.5"x2" then the density would be * increased (probably to 1.5). * * @see #DENSITY_DEFAULT */ public float density; /** * A scaling factor for fonts displayed on the display. This is the same * as {@link #density}, except that it may be adjusted in smaller * increments at runtime based on a user preference for the font size. */ public float scaledDensity; 复制代码
屏幕适配问题的本质是使得布局、布局组件在Android不一样尺寸、不一样分辨率的手机上具有相同的显示效果,下面我将分几个方面来谈谈如何去适配。
3.1.1 使用密度无关像素指定尺寸
因为各类屏幕的像素密度都有所不一样,所以相同数量的像素在不一样设备上的实际大小也会有所差别,这样使用像素(px)定义布局尺寸就会产生问题。 所以,请务必使用密度无关像素 dp 或独立比例像素 sp 单位指定尺寸。
备注 : 在生产过程当中,厂家不会彻底按照屏幕密度标准去生产Android设备,会在Google的标准周围浮动变化,或是偏离Google的屏幕密度标准比较大,再加上理论计算(开方)形成的偏差,实际上使用dp做为单位是不能完彻底全的完成适配操做;
3.1.2 使用相对布局或线性布局,不要使用绝对布局
对于线性布局(Linearlayout)、相对布局(RelativeLayout)、帧布局(FrameLayout)、绝对布局(AbsoluteLayout)以及新增的增强版帧布局(CoordinatorLayout)须要根据需求进行选择,没有绝对而言。
但由于RelativeLayout讲究的是相对位置,即便屏幕的大小改变,视图以前的相对位置都不会变化,与屏幕大小无关,灵活性很强,而LinearLayout法准确地控制子视图之间的位置关系,只能简单的一个挨着一个地排列,因此,对于屏幕适配来讲,使用相对布局(RelativeLayout)将会是更好的解决方案,至于绝对布局因为适配性极差,因此极少使用。
3.1.3 使用wrap_content、match_parent、权重
使用 “wrap_content” 和 “match_parent” 尺寸值而不是硬编码的尺寸,系统会自动计算相应的数值,视图就会相应地使用自身所需的空间或填满可用空间,让布局正确适应各类屏幕尺寸和屏幕方向,组件的权重比同理。
3.1.4 使用minWidth、minHeight、lines等属性
不少时候咱们显示的数据都是由后台返回的,再由咱们加工处理后去适配咱们的组件,这些数据的长度咱们是没法肯定的,而正常状况下咱们构思的布局都仅是适用于理想的状况下,为了保证界面的对齐、数据显示完整等等的缘由,咱们须要在构思布局时增长对组件最小宽高度、行数等属性的设置,确保在特殊的数据下不会破坏咱们的总体布局。
3.1.5 dimens使用
组件的长宽咱们能够经过dimens来定义,不一样的屏幕尺寸能够定义不一样的数值,或者是不一样的语言显示咱们也能够定义不一样的数值,由于翻译后的长度通常都不会跟中文的一致。
以上几种方式能够解决屏幕适配性的问题,可是那些经过伸缩控件来适应各类不一样屏幕大小的布局,未必就是提供了最好的用户体验。你的应用程序应该不只仅实现了可自适应的布局,还应该提供一些方案根据屏幕的配置来加载不一样的布局,能够经过配置限定符(configuration qualifiers)来实现。配置限定符容许程序在运行时根据当前设备的配置自动加载合适的资源(好比为不一样尺寸屏幕设计不一样的布局)。
请注意第二种布局名称目录中的 large 限定符。系统会在属于较大屏幕(例如 7 英寸或更大的平板电脑)的设备上选择此布局。系统会在较小的屏幕上选择其余布局(无限定符)。
也就是说,对于最小宽度大于等于 600 dp 的设备,系统会选择 layout-sw600dp/main.xml(双面板)布局,不然系统就会选择 layout/main.xml(单面板)布局。
但 Android 版本低于 3.2 的设备不支持此技术,缘由是这些设备没法将 sw600dp 识别为尺寸限定符,所以咱们仍需使用 large 限定符。这样一来,就会有一个名称为 res/layout-large/main.xml 的文件(与 res/layout-sw600dp/main.xml 同样)。可是没有太大关系,咱们将立刻学习如何避免此类布局文件出现的重复。
3.2.3 使用布局别名
最小宽度限定符仅适用于 Android 3.2 及更高版本。所以,若是咱们仍需使用与较低版本兼容的归纳尺寸范围(小、正常、大和特大)。例如,若是要将用户界面设计成在手机上显示单面板,但在 7 英寸平板电脑、电视和其余较大的设备上显示多面板,那么咱们就须要提供如下文件:
res/layout/main.xml: 单面板布局 res/layout-large: 多面板布局 res/layout-sw600dp: 多面板布局
后两个文件是相同的,由于其中一个用于和 Android 3.2 设备匹配,而另外一个则是为使用较低版本 Android 的平板电脑和电视准备的。
要避免平板电脑和电视的文件出现重复(以及由此带来的维护问题),您可使用别名文件。例如,您能够定义如下布局:
res/layout/main.xml,单面板布局
res/layout/main_twopanes.xml,双面板布局
而后添加这两个文件:
res/values-large/layout.xml:
res/values-sw600dp/layout.xml:
复制代码
后两个文件的内容相同,但它们并未实际定义布局。它们只是将 main 设置成了 main_twopanes 的别名。因为这些文件包含 large 和 sw600dp 选择器,所以不管 Android 版本如何,系统都会将这些文件应用到平板电脑和电视上(版本低于 3.2 的平板电脑和电视会匹配 large,版本高于 3.2 的平板电脑和电视则会匹配 sw600dp)。
3.2.4 使用屏幕方向限定符
某些布局会同时支持横向模式和纵向模式,但咱们能够经过调整优化其中大部分布局的效果。在新闻阅读器示例应用中,每种屏幕尺寸和屏幕方向下的布局行为方式以下所示:
小屏幕,纵向:单面板,带徽标
小屏幕,横向:单面板,带徽标
7 英寸平板电脑,纵向:单面板,带操做栏
7 英寸平板电脑,横向:双面板,宽,带操做栏
10 英寸平板电脑,纵向:双面板,窄,带操做栏
10 英寸平板电脑,横向:双面板,宽,带操做栏
电视,横向:双面板,宽,带操做栏
所以,这些布局中的每一种都定义在了 res/layout/ 目录下的某个 XML 文件中。为了继续将每一个布局分配给各类屏幕配置,该应用会使用布局别名将二者相匹配:
res/layout/onepane.xml:(单面板)
res/layout/onepane_with_bar.xml:(单面板带操做栏)
复制代码
res/layout/twopanes.xml:(双面板,宽布局)
复制代码
res/layout/twopanes_narrow.xml:(双面板,窄布局)
复制代码
既然咱们已定义了全部可能的布局,那就只需使用配置限定符将正确的布局映射到各类配置便可。
如今只需使用布局别名技术便可作到这一点:
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:
屏幕密度 | 对应的图片大小 | 图片资源目录 |
---|---|---|
120dip | 36px * 36px | mipmap-ldpi |
160dip(基准) | 48px * 48px | mipmap或者mipmap-mdpi |
240dip(1.5倍) | 72px * 72px | mipmap-hdpi |
320dip (2倍) | 96px * 96px | mipmap-xhdpi |
480dip (3倍) | 144px * 144px | mipmap-xxhdpi |
640dip (4倍) | 192px * 192px | mipmap-xxxhdpi |
3.3.2 普通图片和图标
建议安装官方的密度类型进行切图便可,但通常咱们只需xxhdpi或xxxhdpi的切图便可知足咱们的需求;
3.3.3 自动拉伸位图:Nine-Patch的图片类型
支持不一样屏幕大小一般状况下也意味着,你的图片资源也须要有自适应的能力。例如,一个按钮的背景图片必须可以随着按钮大小的改变而改变。 若是你想使用普通的图片来实现上述功能,你会发现这是难以实现的,由于运行时会均匀地拉伸或压缩你的图片。解决方案是使用nine-patch图片,它是一种被特殊处理过的PNG图片,你能够指定哪些区域能够拉伸而哪些区域不能够。
3.3.4 动画、自定义view、shape
可使用代码进行控制和展现多种视图,如patch动画替代帧动画。
3.3.5 ImageView的ScaleType适配
在代码中使用Google提供的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,这样一来获得的数据就和实际预想的更为接近!!
本地加载图片前判断手机分辨率或像素密度,向服务器请求对应级别图片。
通过上面的介绍,相信你们对于屏幕的适配都有了必定的了解,但实际上咱们并不会彻底去执行上面的所有操做,而是须要根据咱们的项目需求去选择最合适的方法去适配。例如说你的产品是针对老年人的,你的字体单位是使用sp仍是dp呢?又好比说RelativeLayout和权重能较好的解决适配的问题,但实际上它们消耗更多的性能,如何去衡量性能与适配的度呢? 知识是死的,人是活的,能灵活运用相关知识方显真本事。