android手机五花八门,那么对于我们这些做android开发的人来说,屏幕适配的问题,显然是无法躲避的问题
1.首先就是使用的单位的问题,应该尽量使用dp和sp
屏幕尺寸: 也就是我们平时所说的某某手机是几寸屏, 比如HTC one V这款手机是3.7寸的, 这里的寸说的是英寸(inch),国际上习惯使用的单位,1inch = 2.54cm,3.7寸指的是屏幕的对角线的长度。
屏幕分辨率: 指屏幕的宽和高的像素数, 比如HTC one V是480*800的。
屏幕密度: 每inch的像素数,比如HTC one V, 是252 px/inch。
px: 像素。一块显示屏是由很多的光点组成的,每一个光点就是一个像素。由于这些光点很小很密,想想看,在上面提到的3.7寸的手机上,横向有480个光点,纵向有800个光点,所以显示出来的文字或者图片才很细腻平滑。
ppi: 和屏幕密度一个意思, 全称是pixel per inch. 是专业一点的叫法.
dpi: dot per inch,每英寸的点数。在电子显示范畴内它和PPI是一个意思。 只有在打印时这个缩写才有意义,在打印领域不存在 PPI的叫法,只说DPI,它表示打印机每英寸打印几个像素点。宽高同样像素下,dpi越大,打印出来的图案越小。
dip: 或者叫dp,这是Android开发中特有的一种度量,称作屏幕无关像素, 它不表示任何具体的长度或者像素点, 这个值只有在 具体屏幕密度的手机上,才会被转换为具体的像素值。 这个时候才会有实际意义。
dip和dp是一个意思,都是Density Independent Pixels的缩写,即密度无关像素,上面我们说过,dpi是屏幕像素密度,假如一英寸里面有160个像素,这个屏幕的像素密度就是160dpi,那么在这种情况下,dp和px如何换算呢?在Android中,规定以160dpi为基准,1dip=1px,如果密度是320dpi,则1dip=2px,以此类推。
假如同样都是画一条320px的线,在480*800分辨率手机上显示为2/3屏幕宽度,在320*480的手机上则占满了全屏,如果使用dp为单位,在这两种分辨率下,160dp都显示为屏幕一半的长度。这也是为什么在Android开发中,写布局的时候要尽量使用dp而不是px的原因。
而sp,即scale-independent pixels,与dp类似,但是可以根据文字大小首选项进行放缩,是设置字体大小的御用单位。
虽然说dp可以去除不同像素密度的问题,使得1dp在不同像素密度上面的显示效果相同,但是还是由于Android屏幕设备的多样性,如果使用dp来作为度量单位,并不是所有的屏幕的宽度都是相同的dp长度,比如说,Nexus S和Nexus One属于hdpi,屏幕宽度是320dp,而Nexus 5属于xxhdpi,屏幕宽度是360dp,Galaxy Nexus属于xhdpi,屏幕宽度是384dp,Nexus 6 属于xxxhdpi,屏幕宽度是410dp。所以说,光Google自己一家的产品就已经有这么多的标准,而且屏幕宽度和像素密度没有任何关联关系,即使我们使用dp,在320dp宽度的设备和410dp的设备上,还是会有90dp的差别。
通过android中线性布局中的属性(layout_weight权重) 按比例来分配,已达到适配效果。
<code class="language-xml hljs has-numbering"><span class="hljs-tag"><<span class="hljs-title">Button </span> <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"0dp"</span> <span class="hljs-attribute">android:layout_weight</span>=<span class="hljs-value">"1"</span> <span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"wrap_content"</span>/></span></code>3.图片适配
不同像素密度的手机加载工程资源文件(res)中不同的资源图片
一般性的图片我们只切一两个典型密度屏幕的图片。但是apk是有可能会运行在从ldpi到xxhdpi的各种级别的手机上。这个时候就需要根据一定的策略去寻找图片了。
Android系统寻找图片的步骤是这样的:
1, 去屏幕密度对应的目录去找。如果找到就拿来用。
2, 如果没找到,就去比这个密度高一级的目录里面去找,如果找到就拿来用。
3, 如果没找到就继续往上找。以此类推。
4, 如果到了xxhdpi目录还没有找到的话,就会去比自身屏幕密度低一级的目录去找,如果低一级的目录>=hdpi,找到了就拿来用。
5, 如果没找到, 就去mdpi目录去找, 如果找到了,就拿来用。
6, 如果没找到,就去默认的drawble目录里去找, 如果找到了就拿来用。
7 ,如果没找到,再去最低的ldpi目录里去找。如果找到了,就拿来用。
8, 如果没找到, 那就是没找到了, 图片无法显示。(不过一般不会出现这种现象,因为如果每个目录都没有这个图片的话,你是编译不过的)
这里有两点需要注意:
① 首先会去比自己密度高的目录里去找,这是因为因为系统相信,你在密度更高的目录里会放置分辨率更大的图片,这样的话这个图片会被缩小,但同时显示效果不会有损失,但是如果优先去低一级别的目录去找的话, 找到的图片就会被放大,这样的话这个图片就会被拉扯模糊了。
e.g. 同一张图片,你在mdpi和xxhdpi目录各放了一份, 这个应用你现在运行在hdpi的手机上, 那应用会选择哪张图片呢。答案是xxhdpi目录里的。即便hdpi离mdpi更近一点!
②,如果在mdpi里找不到是不会直接去ldpi里找的, 而是先去默认的drawble目录里找,这是drawble目录和drawble-mdpi是一个级别的。
4.layout适配
不同分辨率的手机,加载不同的布局文件以达到适配效果。
创建多个layout(如:layout-1280x720、layout-800x480)文件夹用于存放不同像素密度手机所需布局文件。
5.java代码适配
<code class="language-JAVA hljs cs has-numbering">TextView tv = (TextView) findViewById(R.id.tv); <span class="hljs-comment">//获取封装当前手机屏幕信息对象,用于存放宽高值</span> DisplayMetrics metrics = <span class="hljs-keyword">new</span> DisplayMetrics(); <span class="hljs-comment">//给当前屏幕设置宽高 </span> getWindowManager().getDefaultDisplay().getMetrics(metrics); <span class="hljs-comment">//获取屏幕高度 </span> <span class="hljs-keyword">int</span> srceenHeight = metrics.heightPixels; <span class="hljs-comment">//获取屏幕宽度 </span> <span class="hljs-keyword">int</span> srceenWidth = metrics.widthPixels; <span class="hljs-comment">//宽高各占50%</span> RelativeLayout.LayoutParamslayoutParams = <span class="hljs-keyword">new</span> RelativeLayout.LayoutParams( (<span class="hljs-keyword">int</span>)(srceenWidth*<span class="hljs-number">0.5</span>+<span class="hljs-number">0.5</span>),(<span class="hljs-keyword">int</span>)(srceenHeight*<span class="hljs-number">0.5</span>+<span class="hljs-number">0.5</span>)); tv.setLayoutParams(layoutParams);</code>6.dimen适配
dimens.xml存在于工程资源(res)文件夹中不同values(如:value-1280x720、value-800x480、values-xhdpi)文件夹下,可用于指定控件大小,不同像素密度手机加载不同values文件夹下的dimens.xml文件,通常用dimens适配,需要写多个文件,去适配市面上主流的机型。
还有一种就是,给1-2000dp自动生成dimen值,(3.0以上)每一套都用sw-xxxdp(最低屏幕宽度)自动计算1-2000对应的dp,在布局文件中使用该值,这样就是增加了新的分辨率和布局,dimen文件会自动沿用之前的,比较方便