参考
Android多屏幕适配学习笔记
Android官方提供的支持不同屏幕大小的全部方法
Android屏幕适配方案
Android百分比布局库(percent-support-lib) 解析与扩展
官方适配教程 Develop/API Guide/Best Practices/Supporting Multiple Screens
基本概念
DisplayMetrics dm = getResources().getDisplayMetrics();
屏幕尺寸: 也就是我们平时所说的某某手机是几寸屏,1 inch = 2.54cm,5寸指的是屏幕的对角线的长度。
屏幕分辨率: 指屏幕的宽和高的像素数, 比如华为荣耀3c是720×1280px的。//int screenWidth = dm.widthPixels; int screenHeight = dm.heightPixels;
屏幕密度(dpi/ppi): 每inch的像素数,比如华为荣耀3c 320px/inch。 //int densityDPI = dm.densityDpi;
px: 像素一块显示屏是由很多的光点组成的,每一个光点就是一个像素。由于这些光点很小很密,想想看,在上面提到的3.7寸的手机上,横向有480个光点,纵向有800个光点,所以显示出来的文字或者图片才很细腻平滑。
dip(dp):或者叫dp,这是Android开发中特有的一种度量,称作屏幕无关像素, 它不表示任何具体的长度或者像素点
系数: google把160dpi当做标准,dpi/160就是这个缩放系数 //float density = dm.density;
sp: 与dp类似,但是可以根据文字大小首选项进行放缩,是设置字体大小的御用单位。
px dp之间互相转化
scale = dpi/160;
px = dp * scale;
ldpi Resources for low-density (ldpi) screens (~120dpi).
mdpi Resources for medium-density (mdpi) screens (~160dpi). (This is the baseline density.)
hdpi Resources for high-density (hdpi) screens (~240dpi).
xhdpi Resources for extra-high-density (xhdpi) screens (~320dpi).
xxhdpi Resources for extra-extra-high-density (xxhdpi) screens (~480dpi).
xxxhdpi Resources for extra-extra-extra-high-density (xxxhdpi) uses (~640dpi). Use this for the launcher icon only, see note above.
可以得到:
屏幕密度 | 系数 |
---|---|
mdpi | 1 |
hdpi | 1.5 |
xhdpi | 2 |
xxhdpi | 3 |
xxxhdpi | 4 |
目前常见分辨率
480*800 dp表示:320dp*533.3dp hdpi 系数:1.5
720*1280 dp表示:360dp*640dp xhdpi 系数:2
1080*1920 dp表示:360dp*640dp xxhdpi 系数:3
说明:1080*1920跟720*1280屏幕显示没问题,但是480*800屏幕上显示就会有点问题了。我们公司目前UI设计就是以1080×1920分辨率作为标准设计的。所以经常会碰到屏幕适配问题。同时图片为了节省资源,都是放在drawable-xxhdpi下,所以如果不是xxhdpi的屏幕,会对图片进行比例缩放
常用方法
单位转换
//android.util.TypedValue类提供了一个函数,支持把所有的单位换算到px
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;
}
获取屏幕宽度
获取屏幕密度,宽度方法
下面介绍一个google推荐使用的方法
DisplayMetrics dm = getResources().getDisplayMetrics(); //推荐使用,简单粗暴
float density = dm.density;// 屏幕密度(像素比例:0.75/1.0/1.5/2.0)
int densityDPI = dm.densityDpi;// 屏幕密度(每寸像素:120/160/240/320)
float xdpi = dm.xdpi;
float ydpi = dm.ydpi;
Log.d(TAG + " DisplayMetrics", "xdpi=" + xdpi + "; ydpi=" + ydpi);
Log.d(TAG + " DisplayMetrics", "density=" + density + "; densityDPI=" + densityDPI);
int screenWidth = dm.widthPixels; // 屏幕宽(像素,如:480px)
int screenHeight = dm.heightPixels; // 屏幕高(像素,如:800px)
Log.d(TAG + " DisplayMetrics", "screenWidth=" + screenWidth + "; screenHeight=" + screenHeight);
Configuration config = getResources().getConfiguration();
int screenWidthDp = config.screenWidthDp; // 屏幕宽(单位是dp)
int screedHeightDp = config.screenHeightDp; // 屏幕高(单位是dp)
int desityDpi = config.densityDpi;
Log.d("LiaBin", "screenWidthDp=" + screenWidthDp + " screedHeightDp=" + screedHeightDp + " desityDpi=" + desityDpi);
我的手机上打印结果如下:
10-13 17:05:10.486 31170-31170/lbb.mytest.demo D/LiaBin: xdpi=320.0; ydpi=320.0
10-13 17:05:10.487 31170-31170/lbb.mytest.demo D/LiaBin: density=2.0; densityDPI=320
10-13 17:05:10.487 31170-31170/lbb.mytest.demo D/LiaBin: screenWidth=720; screenHeight=1280
10-13 17:05:10.487 31170-31170/lbb.mytest.demo D/LiaBin: screenWidthDp=360 screedHeightDp=615 desityDpi=320
此时可以看出我的手机分辨率720*1280px(360*640dp至于打印的结果615而不是640原因是有状态栏或者是虚拟按键吧) dpi=320 系数320/160=2
注意:config.screenWidthDp/screenHeightDp返回的都是以dp作为单位屏幕宽高,很有用
使用dp存在的问题
使用dp虽然解决了视觉上大小一致的问题,比如80dp的view在160dp宽的屏幕上显示一半(80px),在320dp屏幕上显示的显示效果也是一半(80dp*scale=(320/160)=160px)。
当设备的物理尺寸存在差异的时候,dp就显得无能为力了。为4.3寸屏幕准备的UI,运行在5.0寸的屏幕上,很可能在右侧和下侧存在大量的空白。而5.0寸的UI运行到4.3寸的设备上,很可能显示不下。如下举例
<RelativeLayout
android:id="@+id/layout"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/scroll_but"
android:layout_width="150dp"
android:layout_height="match_parent"
android:background="#b3efe8"
android:text="Scroll But"/>
<Button
android:layout_width="200dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:background="@android:color/holo_green_dark"
android:text="Button 2"/>
</RelativeLayout>
按照Nexus 5(1080*1920) xxhdpi 中间还有一个10dp的空白,xxhdpi说明系数是3,那么屏幕宽就是1080px/3=360dp,所以中间就留有10dp的空白
但是如果是在Nexus S和Nexus One(480*800) hdpi(系数是1.5),却不够显示了,因为此时屏幕总宽度是480/1.5=320,比两个button加起来的350dp还要小,所以不够显示
解决这个问题的方案:
使用百分比
参考
Android屏幕适配方案 http://blog.csdn.net/lmj623565791/article/details/45460089
Android百分比布局库(percent-support-lib) 解析与扩展 http://blog.csdn.net/lmj623565791/article/details/46695347
Tips
以下内容为转载
资源缺失情况下的适配
app运行时,系统会根据属性选择适配的资源进行展示。如果有符合的资源则使用,反之,当符合的资源不存在时,系统会去寻找最相近的可用资源来代替。但是,查找的属性不同,查找的顺序会有所有差异。对dpi属性来说,查找的顺序为,高dpi的资源优先。例如,没能找到hdpi的图片资源,则系统的搜索顺序是drawable-xhdpi->drawable-xxhdpi->drawable-mdip->drawable->drawable->drawable-ldpi。这里drawable被认为比drawable-ldpi更接近hdpi。
另外,对于图片资源在找到相近的资源后,系统还需要对图片进行缩放才会进行使用(否则,可能出现显示不下,或者图片过小的问题,缩放比例是多少,根据dpi的比例来计算缩放比例)。上文的例子中,如果在drawable-xhdpi中找到了资源,那么找到的图片会先缩小到0.75倍以后再使用;如果是在drawable-mdpi中找到了资源,那么图片需要放大到1.5倍以后再使用(放大肯定会造成图片模糊,可能这就是高dpi资源优先的原因所在)。
所以如果你把一张500*500px的图片放在了drawable文件夹下,然后imageview使用wrap-content自适应大小,那么在1080×1920(系数3)的屏幕上显示,那么该图片就被放大到了1500px*1500px,那么屏幕宽都显示不下这张图片了。尽量不要把图片放在drawable中,最好放在drawable-xxhdpi,让图片进行自动压缩,不失真。放大图片会失真。所以设计的时候,最好按照大屏幕来设计切图
有时候,我们的图片资源不一定是从drawable文件夹中读取的,还有可能是从sd卡上读取的,或者从网络上下载的。这个时候,我们需要注意,默认情况下,通过BitmapFactory.decodeFile()函数生成的图片被认为是MDPI的,如果想让图片也获得与drawable文件夹相似的缩放能力,则需要通过BitmapFactory.Option.inDensity属性设置(例如如果图片是为hdpi准备的,则设置为240)。
对于screan size,查找的顺序则是小尺寸优先,大尺寸放弃。例如,在Galaxy note 2上执行apk时,如果未能找到layout-large资源,则查找顺序为:layout-normal->layout->layout-small,不会查找layout-xlarge。
个人对于多设备UI适配的理解
以下是个人的一点总结:
I. 使用简洁的风格来设计UI,让界面变得简单并且一体化,使UI有更加的自适应能力。
II. 尽量使用match_parent,wrap_content等属性来实现实现UI的自适应,减少dp的使用,尽量不要使用px。
III. 如果使用dp,那么不要在layout文件中显示的设定数值,而是通过dimens文件来引用,不同设备上就可以使用同一份layout,而通过不同的dimens来适配。
IV. 可以的情况下,尽量使用.9的png文件,通过无损的缩放来适应UI。
V. 有些在XML上很难设定的UI细节,可能可以通过java代码动态调整的方案来解决。
VI. 如果有需要的话,可以通过w720dp,h360dp,1024x768等属性来对市面上销量比较好的手机做针对性的UI适配。
VII. 没有真机的情况下,可以通过SDK内的模拟器和网上的在线模拟器检查UI效果。
VIII.对于某些app,可以使用Html5来开发UI(即以app内嵌WebView控件来展示Html5),可能可以获得更加的UI适应效果