Android适配
学之广在于不倦,不倦在于固志。 ——晋·葛洪
(学问的渊博在于学习时不知道厌倦,而学习不知厌倦在于有坚定的目标)
001.至于Android适配网上各位大牛各显其招,我这里copy一份鸿洋大神的一份:
---> 相关概念:
>>> 屏幕尺寸:指屏幕的对角线长度,单位是英寸,1英寸=2.54cm;
>>> 屏幕分辨率:指在纵横方向上的像素点数,单位是px,1px=1个像素点。一般屏幕分辨率是纵向像素点*横向像素点,如1280*720
>>> 屏幕像素密度:指每英寸上的像素点数,单位dpi,即"dot pre inch"的缩写。屏幕像素密度与屏幕尺寸和屏幕分辨率有关,在单一变化条件下,屏幕尺寸越小屏幕分辨率越高,像素密度越大,反之越小
>>> dip和dp:两者是同一个东西,都是"Density Independent Pixels"的缩写,即密度无关像素
密度无关像素分析:
1).假如1英寸里面有160个像素,这个屏幕的像素密度就是160dpi,那么在这种情况下,由于在Android中,规定以160dpi为基准,1dp=1px;如果屏幕像素密度是360dpi,则1dp=2px;以此类推计算可以得出所有的dp值
2).例如同样划一条320px的线,在480*800的分辨率的手机上显示为屏幕的2/3宽度,在320*480的手机上则占满了全屏,如果使用dp作为单位,在这两种分辨率下,160dp都是显示屏幕一半的长度,因此,我们在布局中需要把单位设置为dp,谷歌官方也要求写成dp
>>> sp:"scale-independent pixels"的缩写,与dp类似,但是可以根据文字的大小首选项进行缩放,标准字体单位
---> 引入百分比适配:
>>> 经过上面的分析我们知道,为了规避不同像素密度带来的适配问题,谷歌官方推荐使用dp来代替px作为控件长度的度量单位,但是,我们来看一个场景,假如我们以Nexus5作为书写代码时查看效果的测试机型,Nexus5的总宽度为360dp(可以通过上面的分析得出),我们现在需要在水平方向上放置两个按钮,一个是150dp左对齐,另一个是200dp右对齐,中间留有10dp的间隔,在Nexus5上面显示是正常的;但是在NexusS或者NexuOne上显示就不正常了,两个按钮发生了重叠
分析原因:dp虽然可以去除不同像素密度的问题,使得1dp在不同的像素密度上显示效果是相同的,但是,由于Android屏幕设备的多样性,如果使用dp来作为度量单位,并不是所有的屏幕宽度都是相同的dp长度,比如说,NexusS和NexusOne同属于hdpi,屏幕宽度是320dp,而Nexus5属于xxhdpi,屏幕宽度是360dp,Galaxy Nexus属于xhdpi,屏幕宽度是384dp...
所以说,谷歌自己一家的产品(Nexus系列)就已经有这么多的标准,而且屏幕宽度和像素密度没有任何关联关系,即使我们使用dp,在320dp宽度的设备上和384dp的设备上,还是会有64dp的差别。由此就导致了上面显示问题!
>>> 基于上面的问题,我们就采取一种方法来统一单位,不管分辨率是多大,屏幕宽度用一个固定的值的单位来统计,于是,百分比适配就产生了:
我们自定义一个标准(以UI切图的分辨率,比如1280*720),我们就假设手机屏幕的宽度都是720px,那么我们将一个屏幕宽度的总像素平均分成720份,每一份对应1个像素值(即1px),哈哈,这样就满足了适配...
---> 这份代码是copy鸿洋大神的,略加修改,因为我们UI是基于1280*720切的图,不建议做这么多分辨率的适配(体积会增大许多),我只是测试时用的这么多
/** * Created by Okamiy on 2018/5/7. */ public class ScreenUtils { private final static String rootPath = "C:\\Users\\Administrator\\Desktop\\layoutroot\\values-{0}x{1}\\"; private final static float dw = 720f; private final static float dh = 1280f; private final static String WTemplate = "<dimen name=\"x{0}\">{1}px</dimen>\n"; private final static String HTemplate = "<dimen name=\"y{0}\">{1}px</dimen>\n"; public static void main(String[] args) { makeString(480, 854); makeString(480, 800); makeString(540, 960); makeString(720, 1184); makeString(720, 1208); makeString(720, 1280); makeString(720, 1440); makeString(752, 1280); makeString(1080, 1776); makeString(1080, 1788); makeString(1080, 1794); makeString(1080, 1800); makeString(1080, 1808); makeString(1080, 1812); makeString(1080, 1920); makeString(1080, 2160); makeString(1080, 2280); makeString(1440, 2560); makeString(1440, 2960); } public static void makeString(int w, int h) { StringBuffer sb = new StringBuffer(); sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); sb.append("<resources>"); float cellw = w / dw; for (int i = 1; i < 720; i++) { sb.append(WTemplate.replace("{0}", i + "").replace("{1}", change(cellw * i) + "")); } sb.append(WTemplate.replace("{0}", "720").replace("{1}", w + "")); sb.append("</resources>"); StringBuffer sb2 = new StringBuffer(); sb2.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); sb2.append("<resources>"); float cellh = h / dh; for (int i = 1; i < 1280; i++) { sb2.append(HTemplate.replace("{0}", i + "").replace("{1}", change(cellh * i) + "")); } sb2.append(HTemplate.replace("{0}", "1280").replace("{1}", h + "")); sb2.append("</resources>"); String path = rootPath.replace("{0}", h + "").replace("{1}", w + ""); File rootFile = new File(path); if (!rootFile.exists()) { rootFile.mkdirs(); } File layxFile = new File(path + "lay_x.xml"); File layyFile = new File(path + "lay_y.xml"); try { PrintWriter pw = new PrintWriter(new FileOutputStream(layxFile)); pw.print(sb.toString()); pw.close(); pw = new PrintWriter(new FileOutputStream(layyFile)); pw.print(sb2.toString()); pw.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } } public static float change(float a) { int temp = (int) (a * 100); return temp / 100f; } }
---> 直接运行即可生成对应的分辨率文件
注意:
---> 必须再给values下面来一份lay_x.xml和lay_y.xml,因为对于没有生成对应分辨率文件的手机,会默认使用values文件夹下面的,如果没有就会报错无法适配。
---> values文件夹下面的lay_x.xml和lay_y.xml里面的单位要改为dp,切记,因为不知道机型的分辨率,所以采用dp能更好的适配(此处lay_x.xml和lay_y.xml的算法,应该是根据UI出图的标准分辨率来转化为dp,比如:我们的UI是1280*720,此时1px=0.5dp,换算关系见下图。这样计算的好处也是更好适配我们没有的分辨率,因为我们项目里面的尺寸都是安装UI切图的这个基准来标注的)。
在Android中,规定以160dpi(即480*320)为基准:1px=1dp;
密度类型 | 代表的分辨率(px) | 屏幕密度(dpi) | 换算(px/dp) | 比例 |
---|---|---|---|---|
低密度(ldpi) | 240x320 | 120 | 1dp=0.75px | 3 |
中密度(mdpi) | 320x480 | 160 | 1dp=1px | 4 |
高密度(hdpi) | 480x800 | 240 | 1dp=1.5px | 6 |
超高密度(xhdpi) | 720x1280 | 320 | 1dp=2px | 8 |
超超高密度(xxhdpi) | 1080x1920 | 480 | 1dp=3px | 12 |
在values下面的文件:
002.使用特别简单:
根据UI给的图进行编写layout.xml文件即可,根据标注的尺寸设置给控件即可,是多少px就写多少px
003.Android适配要学的还很多,这里推荐几个很棒的博文,本文也是提取这几篇博文相关内容整理而成:
赵凯强:Android屏幕适配全攻略(最权威的官方适配指导)
Last.欢迎指正、交流