首次进行平板开发,从一开始就在琢磨适配是怎么弄的,百度、google、群一大圈,还是没人告诉具体是怎么做的,都是基本的概念性的讲述怎么适配,写了一个界面发现存在很大适配问题,最后找到了几篇有价值的博客,文章末尾有链接;
先了解点基本概念:
px:pixel,像素,电子屏幕上组成一幅图画或照片的最基本单元;
dip : Density independent pixels ,设备无关像素,是安卓开发用的长度单位,1dp表示在屏幕像素点密度为160ppi时1px长度;
dp :就是dip;
sp: scale-independent pixel,安卓开发用的字体大小单位,一般情况下可认为sp=dp;
pt: point,点,印刷行业常用单位,等于1/72英寸,ios也适用这个作为大小单位;
ppi: pixel per inch,每英寸像素数,该值越高,则屏幕越细腻;
dpi :dots per inch , 直接来说就是一英寸多少个像素点。常见取值 120,160,240。我一般称作像素密度,简称密度;
density : 直接翻译的话貌似叫 密度。常见取值 1.5 , 1.0 。和标准dpi的比例(160px/inc);
分辨率 : 横纵2个方向的像素点的数量,常见取值 480X800 ,320X480;
这里引用文档的屏幕尺寸限定符描述:
官方文档:https://developer.android.com/guide/practices/screens_support
新的屏幕尺寸限定符(Android3.2 之后引入)
屏幕特性 | 限定符 | 描述 |
最小宽度限定符 | sw<N>dp 例如sw600dp, sw720dp | 屏幕的最小尺寸,就是屏幕可用区域的最小尺寸,是指屏幕可用高度或宽度的最小值(你可以默认是屏幕的最小宽度).你能用这个限定符确保,无论屏幕方向如何,这个限定符修饰下的布局需要的屏幕最小尺寸是Ndp. 例如,如果你的布局在运行时需要的最小屏幕宽度是600dp,则你可以利用这个限定符创建布局资源目录res/layout-sw600dp.只有当屏幕的最小宽度或最小高度是600dp时,系统才会使用这些布局文件或者资源文件.最小屏幕宽度是固定设备的特有屏幕尺寸,当屏幕方向发生变化时,设备的最小宽度值不变. 设备的最小宽度值要考虑屏幕的尺寸和系统UI.例如,如果在屏幕上有一些系统持久化UI元素,则系统的最小宽度值要比实现的屏幕尺寸小一些,因为这些系统的UI元素你的应用是无法使用到的. 当你使用之前的广义限定符是,你可以定义连续的一系列限定符.用最小宽度来决定广义屏幕尺寸是有意义的,是因为宽度是影响你UI设计的关键因素.UI在竖直方向上会经常滚动,但是在水平方向上往往是固定的布局.可见不论是适配手机或者平板,宽度往往都是布局的关键因素.因此,你需要关心你手机上的最小宽度值. |
屏幕可用宽度 | w<N>dp Examples: w720p w1024p | 指定资源使用时需要的最小宽度.当屏幕方向发生变化时,系统会调整这个值,使其始终为你UI显示的宽度. 这个属性经常被用来判断当前是否需要显示多屏布局,因为哪怕用户当前正在使用平板,你也可能不希望用户在平板竖屏时显示多个屏幕的布局样式.这时,你就可以使用这个限定符来标明你布局需要的最小宽度 |
屏幕可用高度 | h<N>dp Examples: h720dp h1024dp etc. | 标明资源使用时需要的最小高度.当屏幕发生旋转时,系统会自动选择当前大的一方作为高度值.大部分应用很少需要这个限定符,因此不做过多讲解 |
我的项目中建立这么几个values文件夹存放dimens(我的项目强制使用横屏,所以后面加了-land),那具体设备是获得那一个文件夹的尺寸呢?每一个文件夹中的尺寸一样吗?难道需要自己创建这么多文件夹里面的dimens文件慢慢添加吗?
如果横竖屏、不同屏幕设备有不同布局界面,也可以对应创建不同的layout文件夹;
第一问题:获取那个文件夹下的资源:
通过公式 sw*160/dpi 计算出结果之后,选择一个比这个结果小,而又最接近这个值的dp(即文件夹下的内容)。
sw*160/dpi计算示例:
比如
分辨率1280*800, sw 是800
分辨率1920*1080, sw 是1080
dpi获取方法:
1、 adb shell getprop ro.sf.lcd_density 获得
2、代码中
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
int dpi = dm.density*160;
分辨率 | sw | dpi= ro.sf.lcd_density | sw *160/dpi = dp |
1280*720 | 720 | 213 | 720*160/213=540.84 = sw480dp |
1280*800 | 800 | 320 | 800*160/320=400 = sw340dp |
1024*768 | 768 | 160 | 768*160/160 =768 = sw720dp |
800*480 | 480 | 120 | 480*160/120 =640= sw600dp |
800*480 | 480 | 160 | 480*160/160 =480= sw480dp |
比如第一条数据,计算sw*160/dpi的值是540.84,就会去找sw540的文件夹资源,找不到就会向下找,找到sw480dp的文件夹
第二个问题:每一个文件夹中的尺寸一样吗?难道需要自己创建这么多文件夹里面的dimens文件慢慢添加吗?一会回答
里面的值肯定不一样,也不需要一个一个计算一个一个添加,创建一个类DimenTool,自动帮你计算同步;
注意⚠️:要添加dp和sp只需要在values文件夹下的dimens文件添加,然后运行DimenTool工具类即可达到同步(可以根据自己需要的尺寸在DimenTool类中修改,里面的换算比例暂时还不知道是怎么换算来的,引用时注意下,网上有很多不同的换算比例)
/**
* Created by zyt on 2018/7/11.
* 自动计算dimens的工具
*/
public class DimenTool {
public static void gen() {
File file = new File("./app/src/main/res/values/dimens.xml");
BufferedReader reader = null;
StringBuilder sw480 = new StringBuilder();
StringBuilder sw600 = new StringBuilder();
StringBuilder sw720 = new StringBuilder();
StringBuilder sw800 = new StringBuilder();
StringBuilder w820 = new StringBuilder();
try {
System.out.println("生成不同分辨率:");
reader = new BufferedReader(new FileReader(file));
String tempString;
int line = 1;
// 一次读入一行,直到读入null为文件结束
while ((tempString = reader.readLine()) != null) {
if (tempString.contains("</dimen>")) {
//tempString = tempString.replaceAll(" ", "");
String start = tempString.substring(0, tempString.indexOf(">") + 1);
String end = tempString.substring(tempString.lastIndexOf("<") - 2);
double num = Double.valueOf(tempString.substring(tempString.indexOf(">") + 1, tempString.indexOf("</dimen>") - 2));
//这里的换算比例现在还不知道为什么是这样,等找到了在更新上来
sw480.append(start).append((int) Math.round(num * 0.6)).append(end).append("\n");
sw600.append(start).append((int) Math.round(num * 0.75)).append(end).append("\n");
sw720.append(start).append((int) Math.round(num * 0.9)).append(end).append("\n");
sw800.append(tempString).append("\n");
w820.append(tempString).append("\n");
} else {
sw480.append(tempString).append("\n");
sw600.append(tempString).append("\n");
sw720.append(tempString).append("\n");
sw800.append(tempString).append("\n");
w820.append(tempString).append("\n");
}
line++;
}
reader.close();
System.out.println("<!-- sw480 -->");
System.out.println(sw480);
System.out.println("<!-- sw600 -->");
System.out.println(sw600);
System.out.println("<!-- sw720 -->");
System.out.println(sw720);
System.out.println("<!-- sw800 -->");
System.out.println(sw800);
String sw480file = "./app/src/main/res/values-sw480dp-land/dimens.xml";
String sw600file = "./app/src/main/res/values-sw600dp-land/dimens.xml";
String sw720file = "./app/src/main/res/values-sw720dp-land/dimens.xml";
String sw800file = "./app/src/main/res/values-sw800dp-land/dimens.xml";
String w820file = "./app/src/main/res/values-w820dp/dimens.xml";
writeFile(sw480file, sw480.toString());
writeFile(sw600file, sw600.toString());
writeFile(sw720file, sw720.toString());
writeFile(sw800file, sw800.toString());
writeFile(w820file, w820.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
public static void writeFile(String file, String text) {
PrintWriter out = null;
try {
out = new PrintWriter(new BufferedWriter(new FileWriter(file)));
out.println(text);
} catch (IOException e) {
e.printStackTrace();
}
out.close();
}
public static void main(String[] args) {
gen();
}
}
最后再补充一些其他适配知识:
我们常说的mdpi、hdpi、xhdpi、xxhdpi、xxxhdpi对应使用的是多少像素密度呢,看下图:
应用启动图标的适配
至少要提供一个xxxhdpi类型的启动图标,因为Android会帮你自动缩小图标到对应的别的分辨率上(放大是会变模糊的)
最后直接推荐使用今日头条的android适配终极武器(修改手机的设备密度 density):
https://github.com/JessYanCoding/AndroidAutoSize
关于比较好的一篇博客:骚年你的屏幕适配方式该升级了!-今日头条适配方案 - 掘金
参考链接:
Android(density屏幕密度)_hhuleaves的博客-CSDN博客_ro.sf.lcd_density
最清晰的Android多屏幕适配方案 - soaringEveryday - 博客园
Android开发中如何获得正确的layout资源(layout-sw480dp layout-sw600dp-land layout-sw720dp-port)_Felix.Ma的博客-CSDN博客