Android 屏幕适配方案,安卓开发
屏幕尺寸、分辨率、像素密度三者关系
一部手机的分辨率是宽 x 高,屏幕大小是以寸为单位,那么三者的关系是:
假设一部手机的分辨率是 1080x1920(px),屏幕大小是 5 寸
5、密度无关像素(dp):
含义:density-independent pixel,叫 dp 或 dip,与终端上的实际物理像素点无关
单位:dp,可以保证在不同屏幕像素密度的设备上显示相同的效果,是安卓特有的长度单位。
场景例子:假如同样都是画一条长度是屏幕一半的线,如果使用 px 作为计量单位,那么在 480x800 分辨率手机上设置应为 240px;在 320x480 的手机上应设置为 160px,二者设置就不同了;如果使用 dp 为单位,在这两种分辨率下,160dp 都显示为屏幕一半的长度。
dp 与 px 的转换:1dp = (dpi / 160 ) * 1px;
6、独立比例像素(sp):
含义:scale-independent pixel,叫 sp 或 sip
单位:sp,字体大小专用单位 Android 开发时用此单位设置文字大小,可根据字体大小首选项进行缩放; 推荐使用 12sp、14sp、18sp、22sp 作为字体大小,不推荐使用奇数和小数,容易造成精度丢失,12sp 以下字体太小。
7、sp 与 dp 的区别:
dp 只跟屏幕的像素密度有关;
sp 和 dp 很类似但唯一的区别是,Android 系统允许用户自定义文字尺寸大小(小、正常、大、超大等等),当文字尺寸是“正常”时 1sp=1dp=0.00625 英寸,而当文字尺寸是“大”或“超大”时,1sp>1dp=0.00625 英寸。类似我们在 windows 里调整字体尺寸以后的效果——窗口大小不变,只有文字大小改变。
追到 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 --
@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 关于布局组件的适配:
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 来定义,不同的屏幕尺寸可以定义不同的数值,或者是不同的语言显示我们也可以定义不同的数值,因为翻译后的长度一般都不会跟中文的一致。
3.2 关于布局的适配:
以上几种方式可以解决屏幕适配性的问题,但是那些通过伸缩控件来适应各种不同屏幕大小的布局,未必就是提供了最好的用户体验。你的应用程序应该不仅仅实现了可自适应的布局,还应该提供一些方案根据屏幕的配置来加载不同的布局,可以通过配置限定符(configuration qualifiers)来实现。配置限定符允许程序在运行时根据当前设备的配置自动加载合适的资源(比如为不同尺寸屏幕设计不同的布局)。
3.2.1 使用 Size 限定符
很多应用会在较大的屏幕上实施“双面板”模式,即在一个面板上显示项目列表,而在另一面板上显示对应内容。平板电脑和电视的屏幕已经大到可以同时容纳这两个面板了,但手机屏幕就需要分别显示。因此,我们可以使用以下文件以便实施这些布局:
1446102583971944.jpg
1446102728277991.jpg
请注意第二种布局名称目录中的 large 限定符。系统会在属于较大屏幕(例如 7 英寸或更大的平板电脑)的设备上选择此布局。系统会在较小的屏幕上选择其他布局(无限定符)。
3.2.2 最小宽度限定符
使用 Size 限定符有一个问题会让很多程序员感到头疼,large 到底是指多大呢?很多应用程序都希望能够更自由地为不同屏幕设备加载不同的布局,不管它们是不是被系统认定为”large”。这就是 Android 为什么在 3.2 以后引入了”Smallest-width”限定符。
最小宽度限定符可让您通过指定某个最小宽度(以 dp 为单位)来定位屏幕。例如,标准 7 英寸平板电脑的最小宽度为 600 dp,因此如果您要在此类屏幕上的用户界面中使用双面板(但在较小的屏幕上只显示列表),您可以使用上文中所述的单面板和双面板这两种布局,但您应使用 sw600dp 指明双面板布局仅适用于最小宽度为 600 dp 的屏幕,而不是使用 large 尺寸限定符。
1446102760481409.jpg
1446102807853454.jpg
也就是说,对于最小宽度大于等于 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:
1446102885594589.jpg
res/values-sw600dp/layout.xml:
1446102894526452.jpg
后两个文件的内容相同,但它们并未实际定义布局。它们只是将 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:(单面板)
1446102945879746.jpg
res/layout/onepane_with_bar.xml:(单面板带操作栏)
1446102982969502.jpg
res/layout/twopanes.xml:(双面板,宽布局)
1446103034115365.jpg
res/layout/twopanes_narrow.xml:(双面板,窄布局)
1446103047709192.jpg
既然我们已定义了所有可能的布局,那就只需使用配置限定符将正确的布局映射到各种配置即可。
现在只需使用布局别名技术即可做到这一点:
res/values/layouts.xml:
1446103188926681.jpg
res/values-sw600dp-land/layouts.xml:
1446103198972935.jpg
res/values-sw600dp-port/layouts.xml:
1446103206749499.jpg
res/values-large-land/layouts.xml:
1446103214277258.jpg
res/values-large-port/layouts.xml:
1446103221453853.jpg
3.2.5 多套 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:
3.3 关于图片的适配:
3.3.1 LOGO 图标
建议按官方标准准备好各个图标;
评论