扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
关键字: 屏幕适配 px dp dpi sp large限定符 .9.png
专注于为中小企业提供成都网站建设、成都网站制作服务,电脑端+手机端+微信端的三站合一,更高效的管理,为中小企业新县免费做网站提供优质的服务。我们立足成都,凝聚了一批互联网行业人才,有力地推动了1000+企业的稳健成长,帮助中小企业通过网站建设实现规模扩充和转变。
前言: 这篇文章依然是我在 [慕课网 ][h]学习 凯子哥 的同名视频 Android-屏幕适配全攻略 ,所记录下来的笔记---凯子哥讲得真的超详细。
[h]: "MOOC"
从上图可以看出,主流的分辨率是前六种:1280×720、1920×1080、800×480、854×480、960×540、1184×720,不过我们有解决方案。看完这篇文章,想必你就可以解决常见的屏幕适配问题。
接下来正式进入正题。
介绍几个在Android屏幕适配上非常重要的名词:
屏幕尺寸 是指屏幕对角线的长度。单位是英寸,1英寸=2.54厘米
屏幕分辨率 是指在横纵向上的像素点数,单位是px,1px=1像素点,一般是纵向像素横向像素,如1280×720
屏幕像素密度 是指每英寸上的像素点数,单位是dpi,即“dot per inch”的缩写,像素密度和屏幕尺寸和屏幕分辨率有关
dip: Density Independent Pixels(密度无关像素)的缩写。以 160dpi 为基准,1dp=1px
dp: 同 dip
dpi: 屏幕像素密度的单位,“dot per inch”的缩写
px: 像素,物理上的绝对单位
sp: Scale-Independent Pixels的缩写,可以根据文字大小首选项自动进行缩放。Google推荐我们使用12sp以上的大小,通常可以使用12sp,14sp,18sp,22sp,最好不要使用奇数和小数。
用于区分不同的像素密度。
在Google官方开发文档中,说明了 ** mdpi:hdpi:xhdpi:xxhdpi:xxxhdpi=2:3:4:6:8 ** 的尺寸比例进行缩放。例如,一个图标的大小为48×48dp,表示在mdpi上,实际大小为48×48px,在hdpi像素密度上,实际尺寸为mdpi上的1.5倍,即72×72px,以此类推。
我们可以通过以下几种方式来支持各种屏幕尺寸:
wrap_content: 根据控件的内容设置控件的尺寸
math_parent: 根据父控件的尺寸大小设置控件的尺寸
weight: 权重,在线性布局中可以使用weight属性设置控件所占的比例
例如,我们要实现下图所显示的效果:当屏幕尺寸改变时,new reader控件两边的控件大小不变,new reader控件会占完剩余的空间。
具体布局文件如下:
小插曲: 关于 android:layout_weight 属性
一般情况,我们都是设置要进行比例分配的方向的宽度为0dp,然后再用权重进行分配。如下:
效果为:
效果为:
button1宽度=L+(L-2L)×1/3=2/3L
button2宽度=L+(L-2L)×2/3=1/3L
当然,还有其他的方式,都可以运用此公式进行计算。
在实际开发中,我们一般使用0dp的方式,而不使用其他方式。
简单的布局一般都使用 线性布局 ,而略微复杂点的布局,我们使用 相对布局 ,大多数时候,我们都是使用这两种布局的嵌套。
我们使用 相对布局 的原因是, 相对布局 能在各种尺寸的屏幕上保持控件间的相对位置。
res/layout/main.xml 单面板:
res/layout-large/main.xml 双面板:
如果这个程序运行在屏幕尺寸大于7inch的设备上,系统就会加载 res/layout-large/main.xml 而不是 res/layout/main.xml ,在小于7inch的设备上就会加载 res/layout/main.xml 。
需要注意的是,这种通过 large 限定符分辨屏幕尺寸的方法,适用于android3.2之前。在android3.2之后,为了更精确地分辨屏幕尺寸大小,Google推出了最小宽度限定符。
res/layout-sw600dp/main.xml ,双面板布局: Small Width 最小宽度
这种方式是不区分屏幕方向的。这种最小宽度限定符适用于android3.2之后,所以如果要适配android全部的版本,就要使用 large 限定符和 sw600dp 文件同时存在于项目 res 目录下。
这就要求我们维护两个相同功能的文件。为了避免繁琐操作,我们就要使用布局别名。
由于后两个文具文件一样,我们可以用以下两个文件代替上面三个布局文件:
res/layout/main.xml 单面板布局
res/layout/main_twopanes.xml 双面板布局
然后在 res 下建立
res/values/layout.xml 、
res/values-large/layout.xml 、
res/values-sw600dp/layout.xml 三个文件。
默认布局
res/values/layout.xml :
Android3.2之前的平板布局
res/values-large/layout.xml :
Android3.2之后的平板布局
res/values-sw600dp/layout.xml :
这样就有了 main 为别名的布局。
在activity中 setContentView(R.layout.main);
这样,程序在运行时,就会检测手机的屏幕大小,如果是平板设备就会加载 res/layout/main_twopanes.xml ,如果是手机设备,就会加载 res/layout/main.xml 。我们就解决了只使用一个布局文件来适配android3.2前后的所有平板设备。
如果我们要求给横屏、竖屏显示的布局不一样。就可以使用 屏幕方向限定符 来实现。
例如,要在平板上实现横竖屏显示不用的布局,可以用以下方式实现。
res/values-sw600dp-land/layouts.xml :横屏
res/values-sw600dp-port/layouts.xml :竖屏
自动拉伸位图,即android下特有的 .9.png 图片格式。
当我们需要使图片在拉伸后还能保持一定的显示效果,比如,不能使图片中的重要像素拉伸,不能使内容区域受到拉伸的影响,我们就可以使用 .9.png 图来实现。
要使用 .9.png ,必须先得创建 .9.png 图片,androidSDK给我们提供了的工具就包含 .9.png 文件的创建和修改工具。双击 SDK安装目录 oolsdraw9patch.bat ,就会打开下图所示的窗口。
下面是一个例子:
Button属性设置:
如果我们选择的内容区域偏差太大,可能就不会显示出text值 BUTTON 。
好了,这篇文章写的有点多了,剩下的内容放在 下篇文章 记录吧。
内容提要:
解决方案-支持各种屏幕密度
解决方案-实施自适应用户界面流程
未完待续
Android的屏幕大同小异,分辨率也是各种各样,手机App上的差异性还没那么明显,基本用Dp weight就可以比较好的适配各种手机。但是在Pad上的表现就不尽如意,而且发现像华为Pad Pro这种高端设备,是可以通过系统设置去设置修改系统的density值,导致整个如果只用一套DpUI布局去实现,会出现很奇怪的UI效果,基本不能适配。这时候就需要对UI进行适配。通过资料查询,需要了解如下的几个概念
1.px,pixel 就是像素,最基本的真实显示单位
2.dp,dip, Density-independent pixel,设备的独立像素,1dp表示在屏幕像素点密度为160ppi时1px长度
3.ppi, pixel per inch ,每英寸对角像素点,这个是物理上的
4.dpi, dot per inch ,每英寸多少个点,这个是软件上的,这里的点跟像素点不同
5.sp: scale-independent pixel, 字体大小单位
简单换算就是
ppi =根号( 横屏像素点平方+纵屏像素点平方)➗对角线的长度,这个长度是一英寸
1dp = (dpi/160) px
然后有些不同尺寸手机的ppi可能是420, 430, 440, 450, 460.,由于物理ppi上是固定的,改变不了,为了适配,通过人为设置一个dpi,来规范这些差不多ppi值,使得这些相差差不多的屏幕都是通用一个dpi,也就是使用同一套设计方案。
一开始通过dp值来实现适配,是可以解决大部分适配问题,但是在遇到pad这种设备,由于是横屏,而且系统设置还可以修改density值,使得用一套固定屏幕(比如1280 * 800)的方向变得不是那么合适。
这时候想到可以通过Android中 dimens中定义dimen值,Android中可以通过sw去搜索对应的dimen值表来获取对应的配置,smallestWidth适配,sw限定符适配,只要我们把对应的表通过换算,得到一个新值,就可以得到在不同的density值中得到对应的dp值表,解决华为上一个设备对应不同density值的问题。
那么问题来了,如果去得到sw不同的dimens呢,网上的方法很多,有些自己写脚本,有些自己写程序生成,为了就是列举所有的值,一般1-1000dp,然后基于一个基准,比如360dp宽度,去换算出不同屏幕宽度的dimens值,这里我推荐Android Studio的插件ScreenMatch,先安装插件
然后在values中创建dimens文件夹,并创建dimens.xml,其中写上自己定义的dp值,如下
然后在该文件右键,选择screenmatch
插件就会生成一堆其他屏幕的dimens文件,并且自动生成1-800的其他dp值,基本满足开发中的定义,如果没有的话,就自行在这里定义,然后重新生成。
关于ScreenMatch的生成还有一个基准,就是基于那个dpi来生成,通过插件生成,在根目录会多出了两个文件,一个example, 一个config文件
这里我们看看properties文件,内容如下
其中base_dp=850就是基于850,然后可以通过match_dp去调整要适配的dpi值。
通过这方方式,会在dimens文件自动生成dimen文件
在网上看到,还可以通过修改density去修改,这种方式有空我在试试
最近参考 今日头条算法 ,优化了项目的屏幕适配策略。下面是适配过程中的一些心得,部分内容来源于网络。
举个例子:屏幕分辨率为:1920*1080,屏幕尺寸为5吋的话,那么dpi为440。
dp就是密度自适应的像素。1dp表示 在dpi为160的设备上的一颗像素
px与dp的换算公式px = dp * (dpi / 160),很显然,由于相同分辨率但不同屏幕大小的设备dpi是不同的,导致px和dp的基本不存在一个固定的换算关系,为了方便屏幕适配,Android设置了6个通用的密度,换算px与dp时采取通用密度计算,而非设备实际的密度。
以下为6种通用密度,以及其最小的分辨率
得到上面通用密度之后,我们换算dp与px多了一种简便方式。Android系统用mdpi(160dpi)作为基准,此时1px = 1dp,又有px = dp * (dpi / 160),所以我们可以很容易的得到以下换算:
sp在dp的基础上引入了scaleFactor变量,一般用于字号,可在系统设置里调大。
同一张图片放到以上4个分辨率类型的文件夹里,在页面上呈现的效果如下
实际呈现的算法为: 图片尺寸 * 系统density / 文件夹 density
因为图片尺寸、系统density都是固定的,因此图片最终尺寸表现为: 图片放的位置越"low",呈现的尺寸越大
比如 图片宽度200px,系统 density =3,则图片宽度
下面是详细的解释
我们知道,不管在布局文件中填写的是什么单位,它最后都会被系统转化为 px。系统的转换算法如下:
可以看到 px = dp*density 。
横向适配的最终目的:让100dp的宽度,在各个机型上,在屏幕上所占的 比例相同 。
其核心算法是px = dp* density。通过修改density这个变量,我们可以让px和画布标注的px值一致,达到适配的效果。
美工同学提供的画布宽度为 750px(iphone6) ,开发中,我们对这些px标注 除2 得到dp值进行使用。
那么density如何求出呢? 根据系统算法px = dp*density,反推 density =px/dp
拿横向适配画布, density对于不同分辨率的手机修改后如下:
375是我们拿UI画布横向分辨率750/2得出。
Android屏幕适配-基础篇
Android屏幕适配-应用篇
从两个大方面阐述一下Android的屏幕适配:
Android推荐使用dp作为尺寸单位来适配UI ,通过dp加上自适应布局和weight比例布局可以基本解决不同手机上适配的问题,这基本是最原始的Android适配方案。
缺点 :
(1)这种方案只能保证我们写出来的界面适配绝大部分手机,部分手机仍然需要单独适配,但dpi的不同,还是会存在差异。
(2)一般的设计稿都是以px为单位的,所以我们在写layout文件的时候需要将px转为dp,影响开发效率。
为了高效的实现UI开发,出现了新的适配方案,我把它称作宽高限定符适配。简单说,就是穷举市面上所有的Android手机的宽高像素值,设定一个基准的分辨率,其他分辨率都根据这个基准分辨率来计算,在不同的尺寸文件夹内部,根据该尺寸编写对应的dimens文件:
鸿洋大神的作品 ,使用也超级简单,核心功能就是在绘制的时候在onMeasure里面做变换,重新计算px。
缺点 :我们自定义的控件可能会被影响或限制,可能有些特定的控件(框架没有做适配的控件),需要单独适配。
小结:上述几种适配方案都是实际开发中用过的方案,但随着技术不断的更新,出现了更好的适配方案。
实现原理 :Android会识别屏幕可用高度和宽度的最小尺寸的dp值( 其实就是手机的宽度值 ),然后根据识别到的结果去资源文件中寻找对应限定符的文件夹下的资源文件。
sw限定符适配 和 宽高限定符适配 类似,区别在于,前者有很好的容错机制,如果没有value-sw360dp文件夹,系统会向下寻找,比如离360dp最近的只有value-sw350dp,那么Android就会选择value-sw350dp文件夹下面的资源文件。这个特性就完美的解决了上文提到的宽高限定符的容错问题。
优点: 1.非常稳定,极低概率出现意外
2.不会有任何性能的损耗
3.适配范围可自由控制,不会影响其他三方库
缺点 :就是多个dimens文件可能导致apk变大,几百k。
附件: 生成sw文件的工具
实现原理 : 修改系统的density值 (核心)
今日头条适配是以设计图的宽或高进行适配的,适配最终是改变系统density实现的。
过程:
AndroidAutoSize 是基于今日头条适配方案,该开源库已经很大程度上解决了今日头条适配方案的两个缺点,可以对activity,fragment进行取消适配。也是目前我的项目中所使用的适配方案。
使用也非常简单只需两步:
(1)引入:
(2)在 AndroidManifest 中填写全局设计图尺寸 (单位 dp),如果使用副单位,则可以直接填写像素尺寸,不需要再将像素转化为 dp,详情请查看 demo-subunits
@[TOC](文章目录)
hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1"
# 前言
font color=#999AAA 使用工具Android studio,利用values文件下dimens.xml界面适配安卓屏幕/font
hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1"
font color=#999AAA 提示:以下是本篇文章正文内容,下面案例可供参考
# 一、概念
1.屏幕分辨率单位是px,例如Android手机常见的分辨率:320x480px、480x800px、720x1280px、1080x1920px。
2.手机屏幕的密度:每英寸的像素点数,单位是dpi。
| 密度类型 |代表的分辨率(px)| 屏幕像素密度(dpi) | 1dp转换为px |
|:--------|:--------|:--------|:--------|
| 低密度(ldpi) |240x320|120|0.75|
| 中密度(mdpi) |320x480|160|1|
| 高密度(hdpi)|480x800|240| 1.5|
| 超高密度(xhdpi)|720x1280|320|2|
| 超超高密度(xxhdpi) |1080x1920|480|3|
3.由于android的机型屏幕大小品类太多了,有一些是不标准的,这时我们就需要单独去获取屏幕的分辨率和密度了。
# 二、获取屏幕的分辨率和密度
```java
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
float density = displayMetrics.density;
int densityDpi = displayMetrics.densityDpi;
int width = displayMetrics.widthPixels;
int height = displayMetrics.heightPixels;
Log.e("123","密度:"+density+"---"+densityDpi);
Log.e("123","屏幕分辨率:"+width+"x"+height);
Log.e("123","安卓系统:"+android.os.Build.VERSION.RELEASE);
Log.e("123","手表型号:"+android.os.Build.PRODUCT);
```
# 三、SmallestWidth适配
**smallestWidth适配,或者叫sw限定符适配。指的是Android会识别屏幕可用高度和宽度的最小尺寸的dp值(其实就是手机的宽度值),然后根据识别到的结果去资源文件中寻找对应限定符的文件夹下的资源文件。**
**sw计算公式:sw = 屏幕宽度 / (dpi/160) 注:160是默认的**
**例如:屏幕宽度为1080px、480dpi 的sw = 1080/(480/160)**
# 四、生成 dimens 文件
1、 首先在 res 目录下新建各种尺寸的 values 文件 。文件名为:values-sw(你要适配屏幕的sw值)dp。
例如:
![code23]()
注意:values文件下也生成 dimens文件
**生成dimens值工具类**
1、先生成标准的值。//value = (i + 1) * 1;
2、再用生成其他的值。 //value = (i + 1) * 需要生成的sw值/标准的sw值;
例如:value = (i + 1) * 160 / 320;
```java
public static void genDimen() {
StringBuilder stringBuilder = new StringBuilder();
try {
double value;
for (int i = 0; i 500; i++) {
//value = (i + 1) * 1; //这里控制对应转换的值,如果是标准尺寸就一对一转换
//value = (i + 1) * 需要生成的sw值/标准的sw值; //这里控制对应转换的值
value = (i + 1) * 1
//value = (i + 1) * 160 / 320;
value = Math.round(value * 100) / 100;
//dp可改成sp
stringBuilder.append("dimen name=\"size_" + (i + 1) + "\"" + value + "dp/dimen\r\n");
}
if (stringBuilder.length() 4000) {
for (int i = 0; i stringBuilder.length(); i += 4000) {
if (i + 4000 stringBuilder.length())
Log.e("123", stringBuilder.substring(i, i + 4000));
else
Log.e("123", stringBuilder.substring(i, stringBuilder.length()));
}
} else {
Log.e("123", stringBuilder.toString());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
```
示例:(我这是以sw320为适配的标准的,你们可改自己的标准)
1、sw320的样例
```java
resources
dimen name="dimen_1"1.0dp/dimen
dimen name="dimen_2"2.0dp/dimen
dimen name="dimen_3"3.0dp/dimen
dimen name="dimen_4"4.0dp/dimen
dimen name="dimen_5"5.0dp/dimen
dimen name="dimen_6"6.0dp/dimen
dimen name="dimen_7"7.0dp/dimen
dimen name="dimen_8"8.0dp/dimen
dimen name="dimen_9"9.0dp/dimen
dimen name="dimen_10"10.0dp/dimen
dimen name="size_1"1.0sp/dimen
dimen name="size_2"2.0sp/dimen
dimen name="size_3"3.0sp/dimen
dimen name="size_4"4.0sp/dimen
dimen name="size_5"5.0sp/dimen
dimen name="size_6"6.0sp/dimen
dimen name="size_7"7.0sp/dimen
dimen name="size_8"8.0sp/dimen
dimen name="size_9"9.0sp/dimen
dimen name="size_10"10.0sp/dimen
/resources
```
2、sw160的样例
```java
resources
dimen name="dimen_1"0.0dp/dimen
dimen name="dimen_2"1.0dp/dimen
dimen name="dimen_3"1.0dp/dimen
dimen name="dimen_4"2.0dp/dimen
dimen name="dimen_5"2.0dp/dimen
dimen name="dimen_6"3.0dp/dimen
dimen name="dimen_7"3.0dp/dimen
dimen name="dimen_8"4.0dp/dimen
dimen name="dimen_9"4.0dp/dimen
dimen name="dimen_10"5.0dp/dimen
dimen name="size_1"0.0sp/dimen
dimen name="size_2"1.0sp/dimen
dimen name="size_3"1.0sp/dimen
dimen name="size_4"2.0sp/dimen
dimen name="size_5"2.0sp/dimen
dimen name="size_6"3.0sp/dimen
dimen name="size_7"3.0sp/dimen
dimen name="size_8"4.0sp/dimen
dimen name="size_9"4.0sp/dimen
dimen name="size_10"5.0sp/dimen
/resources
```
3、xml界面控件使用样例
```java
TextView
android:layout_width="@dimen/dimen_30"
android:layout_height="@dimen/dimen_30"
android:textSize="@dimen/size_20"
android:layout_margin="@dimen/dimen_10"
android:padding="@dimen/dimen_10"
```
hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1"
# 总结
font color=#999999 提示:这里对文章进行总结:
如果你的app需要适配dpi较低的屏幕,最好以最小dpi的sw为适配的标准。
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流