博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Andoid屏幕适配终极手段(小编用过最得劲的dp适配)
阅读量:6893 次
发布时间:2019-06-27

本文共 13586 字,大约阅读时间需要 45 分钟。

@[toc]

小编尝试过2种屏幕适配方法:

1.PX适配

使用PXGenerator代码生成各种分辨率的文件夹以及文件, 以某分辨率比如480x800为基准,1px=1px, 按比例生成其他各种分辨率的dimen文件,会有1px=4px,1px=3px之类的情况, 以次达到屏幕适配的目的

14.7px
16.0px
17.3px
18.7px
20.0px
21.3px
22.7px
24.0px
25.3px
26.7px
28.0px
29.3px
30.7px
32.0px
33.3px
34.7px
36.0px
37.3px
38.7px
复制代码
public class PXGenerator {		private static final String HEAD="
\n";//头部 private static final String START_TAG="
\n";//开始标签 private static final String END_TAG="
\n";//结束标签 private static final String ROOT="F:\\AndroidStudioWorkSpace\\ScreenAdaptation\\app\\src\\main\\res\\values-2560x1800\\";//文件夹 private static final String FILE_NAME="dimen_py2560.xml";//文件名 private static final String path=ROOT+FILE_NAME;//文件路径 private static final float TIMES=1920*1.0f/2560; private static final int DIMENSION=1920;//以次为基准 public static void main(String[] args) { generateXMl(); } private static void generateXMl() { try { File diectoryFile = new File(ROOT); if(!diectoryFile.exists()){ diectoryFile.mkdirs(); } File file = new File(path); if(file.exists()){ file.delete(); } FileWriter fileWriter = new FileWriter(file); fileWriter.write(HEAD); fileWriter.write(START_TAG); for(int i=0;i<=DIMENSION;i++){ String output="\t
"+roundString(i)+"px
\n"; fileWriter.write(output); } fileWriter.write(END_TAG); fileWriter.flush(); fileWriter.close(); System.out.println("写入成功"); } catch (IOException e) { e.printStackTrace(); System.out.println("写入失败"); } } private static String roundString(int data) { String result=""; float floatResult=data/TIMES; DecimalFormat df = new DecimalFormat("0.00"); result = df.format(floatResult); return result; } }复制代码

缺点:需要生成的相关分辨率的dimen实在太多,增大APK包体积

2.DP适配由来

先熟知如下图所示的各种参数

private String getScreenParams() {        DisplayMetrics dm = new DisplayMetrics();        getWindowManager().getDefaultDisplay().getMetrics(dm);        int heightPixels = dm.heightPixels;//高的像素        int widthPixels = dm.widthPixels;//宽的像素        int densityDpi = dm.densityDpi;//dpi        float xdpi = dm.xdpi;//xdpi        float ydpi = dm.ydpi;//ydpi        float density = dm.density;//density=dpi/160,密度比        float scaledDensity = dm.scaledDensity;//scaledDensity=dpi/160 字体缩放密度比        float heightDP = heightPixels / density;//高度的dp        float widthDP = widthPixels / density;//宽度的dp        String str = "heightPixels: " + heightPixels + "px";        str += "\nwidthPixels: " + widthPixels + "px";        str += "\ndensityDpi: " + densityDpi + "dpi";        str += "\nxdpi: " + xdpi + "dpi";        str += "\nydpi: " + ydpi + "dpi";        str += "\ndensity: " + density;        str += "\nscaledDensity: " + scaledDensity;        str += "\nheightDP: " + heightDP + "dp";        str += "\nwidthDP: " + widthDP + "dp";        return str;    }复制代码

1.px:

屏幕分辨率:在橫纵向上的像素点数。单位:px即1px=1个像素点。 一般以纵向像素x横向像素表示,如1920x1080

2.dpi:

屏幕像素密度,指每英寸上的像素点数,dot per inch的缩写,与屏幕尺寸和屏幕分辨率有关。以Nexus5为例,官方参数为1920*1080,dpi=445,4.95 inch 那么,这个445的dpi是怎么算出来的呢?由上面介绍可知,屏幕尺寸4.95是屏幕对角线的长度,而dpi是指每英寸上的像素点数,所以应该由对角线所占的像素值除以4.95,如下:

3.dp/dip:

dp和dip是一样的,密度无关像素,Density Independent Pixels的缩写,以160dpi为基准。在160dpi设备 上1dp=1px,在240dpi设备上1dp=1.5px,以此类推

4.density: density=dpi/160,密度比

5.scaledDensity : scaledDensity=dpi/160 字体缩放密度比

6.heightDP: heightDP = heightPixels / density;//高度的dp

7.widthDP: heightDP = widthPixels / density;//宽度的dp

先看上图sw< N >dp: smallestWidth,最小宽度,

官方提供了多种尺寸限制符,sw指的是最小宽度,和竖屏横屏无关:

如800DPx480DP(最小宽度是480dp,比如部分800x480手机), 592DPx360DP(最小宽度是360dp,比如部分1920x1080手机), 604DPx360DP(最小宽度是360dp,比如部分1920x1080手机), 680DPx360DP(最小宽度是360dp.比如部分2160x1080手机(2018年左右开始流行长屏幕手机、刘海屏手机)), 1232DPx900DP(最小宽度是900dp,比如部分2560x1800手机(平板))

这里发现多种分辨率手机最小宽度都是360DP,尤其是1920x1080,是2018年左右最流行的屏幕分辨率,因此考虑以宽度360dp为基准做适配(这也是为何使用DP适配,而不使用PX适配的原因,因为DP适配需要的dimens文件会少很多)

sw< N >dp会向上兼容:

比如valuse-sw481dp文件夹,当且仅当手机最小宽度dp>=481dp才会去该目录寻找数值,800DPx480DP最小宽度480dp,是无法进入valuse-sw481dp寻找数值的,如果values目录下没有对应的数值,只有一个结果GG=APP崩溃。所以任何时候都记得一定要在values目录下创建对应的数值

800DPx480DP手机,由于没有valuse-sw480dp文件夹,只能向下寻找,找到valuse-sw100dp文件夹,结果界面当然很丑,(适配具体操作 容后再谈)

上文说到:多种分辨率手机最小宽度都是360DP,尤其是1920x1080,是2018年左右最流行的屏幕分辨率,因此考虑以宽度360dp为基准做适配

3.DP适配原理以及具体操作

常见的swdp如下:

//    private static final int[] dps = {360, 384, 392, 400, 410, 411, 480, 533, 592,    //            600, 640, 662, 720, 768, 800, 811, 820,900, 960, 961, 1024, 1280};//常见dp列表复制代码

为了适配更多手机,dp应尽可能包含更多,而且连续性强,间隔小

dp适配原理: 和px适配原理类似(按比例计算),以sw360dp为基准,

48dp
18sp
12dp
360dp
10sp
30dp
10dp
14sp
40dp
70dp
100dp
80dp
12sp
60dp
-10dp
4.5dp
-5.5dp
复制代码

那么sw720dp如下: 全是2倍值

96dp
36sp
24dp
720dp
20sp
60dp
20dp
28sp
80dp
140dp
200dp
160dp
24sp
120dp
-20dp
9dp
-11dp
复制代码

具体操作:

1) 以sw360dp为基准,按照1920x1080的UI设计图,可以让设计师用“标你妹”等软件标注dp和sp,也可以上传到蓝湖,蓝湖上的标注更爽,在values下的dimens文件中定义dp值对应的dimen标签。 比如,UI图上标注的一个按钮高度是40dp,那么在dimens文件中创建

40dp
复制代码

name名字可以随便取

2) 将如下代码类放到当前android项目的app module下,记得更改root目录为自己的android项目的目录,

public class DPGeneratorLittle {    private static final String HEAD = "
\n";//头部 private static final String START_TAG = "
\n";//开始标签 private static final String END_TAG = "
\n";//结束标签 private static final float DP_BASE = 360;//360dp为基准 private static final int DP_MAX = 720;//所有dimens文件dp从0生成到这个值 private static final int SP_MAX = 48;//SP最大 private static final int[] dps = {360, 384, 392, 400, 410, 411, 480, 533, 592, 600, 640, 662, 720, 768, 800, 811, 820,900, 960, 961, 1024, 1280};//常见dp列表// private static final int[] dps = {100,481,510,720,900};//常见dp列表 private static final String root="F:\\AndroidStudioWorkSpace\\ScreenAdaptation\\app\\src\\main\\res\\";//生成文件的主目录 private static ExecutorService fixedThreadPool;//线程池,用于生成XML文件 private static int size_thread = 5;//线程池大小 private static DocumentBuilderFactory dbFactory; private static DocumentBuilder db; private static Document document; public static void main(String[] args) { try { dbFactory = DocumentBuilderFactory.newInstance(); db = dbFactory.newDocumentBuilder(); //将给定 URI 的内容解析为一个 XML 文档,并返回Document对象 //记得改成自己当前项目的路径 document = db.parse(root+"values\\dimens.xml"); //按文档顺序返回包含在文档中且具有给定标记名称的所有 Element 的 NodeList NodeList dimenList = document.getElementsByTagName("dimen"); if (dimenList.getLength()==0)return; List
list = new ArrayList<>(); for (int i = 0; i < dimenList.getLength(); i++) { //获取第i个book结点 Node node = dimenList.item(i); //获取第i个dimen的所有属性 NamedNodeMap namedNodeMap = node.getAttributes(); //获取已知名为name的属性值 String atrName = namedNodeMap.getNamedItem("name").getTextContent(); String value = node.getTextContent(); System.out.println("+++atrName++++++++++++++++++++" + atrName); System.out.println("+++++++++++++value++++++++++" + value); list.add(new Dimen(atrName, value)); } fixedThreadPool = Executors.newFixedThreadPool(size_thread); for (int i = 0; i < dps.length; i++) { XMLThread xmlThread = new XMLThread(i, list); fixedThreadPool.execute(xmlThread);//线程启动执行 } } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ParserConfigurationException e) { e.printStackTrace(); } } private static class XMLThread implements Runnable { private int index = 0; private List
list; public XMLThread(int index, List
list) { this.index = index; this.list = list; } @Override public void run() { //记得改成自己当前项目的路径 generateXMl(list, index, root+"values-sw" + dps[index] + "dp\\", "dimens.xml"); } } private static void generateXMl(List
list, int index, String pathDir, String fileName) { try { File diectoryFile = new File(pathDir); if (!diectoryFile.exists()) { diectoryFile.mkdirs(); } File file = new File(pathDir + fileName); if (file.exists()) { file.delete(); } FileWriter fileWriter = new FileWriter(file); fileWriter.write(HEAD); fileWriter.write(START_TAG); //????????????????????????????????????????????? int size = list.size(); String atrName; String value; for (int i = 0; i < size; i++) { atrName = list.get(i).getAtrName(); value = list.get(i).getValue(); String output = "\t
" + roundString(Float.valueOf(value.substring(0, value.length() - 2)), index) + value.substring(value.length()-2)+"
\n"; fileWriter.write(output); } fileWriter.write(END_TAG); fileWriter.flush(); fileWriter.close(); System.out.println("写入成功"); } catch (IOException e) { e.printStackTrace(); System.out.println("写入失败"); } } //精确到小数点后2位,并且四舍五入(因为有SW1280dp,基准是160dp,1dp=1px, // 如果精确到小数点后一位,四舍五入会有0.5dp误差,在sw1280dp中会有4PX误差,精确到小数点后2位,四舍五入,误差控制在1PX之内) private static String roundString(float data, int index) { String result = ""; float floatResult = data * dps[index] / DP_BASE; DecimalFormat df = new DecimalFormat("0.00"); result = df.format(floatResult); return result; } private static class Dimen { private String atrName; private String value; public Dimen(String atrName, String value) { this.atrName = atrName; this.value = value; } public String getAtrName() { return atrName; } public void setAtrName(String atrName) { this.atrName = atrName; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } }}复制代码

如图执行工具类的main方法,即可生成各种dimens文件

3)使用的相关原理及技巧

1.工具类解析values目录下的dimens文件,根据其中所有的dimen标签,name和values,生成其他各种dp对应的dimens文件

2.DP_BASE可修改为自己定的基准(你可以根据实际情况定为其他dp,一般情况下,定义为360dp,因为大部分手机sw都是360dp)

3.root对应的是当前android项目的主目录路径,千万不能出错

4.size_thread,线程池大小默认5,你可以设置其他数值

5.你还可以根据自己强迫症的各种习惯和喜好,各种更改成自己喜欢的风格,代码很精简,修改很容易

6.在每次打包(如果需要调试sw360dp之外的手机,或者上线APP)之前,记得执行DPGeneratorLittle的main方法,以确保所有的dimen文件都是最新的

7.负数的dp,小数的dp,甚至小数的sp(一般没这情况)都可以生成,放心大胆使用

8.如果发现某台手机适配效果不是很好,那么真机调试,得到其swdp,然后在用DPGeneratorLittle生成对应的swdp,dimen文件

9.执行DPGeneratorLittle报错的时候,记得删除所有生成的valuse目录,clean项目,重新执行

4.欣赏DP适配案例

800PXx480PX的手机

1920PXx1080PX的手机
2560PXx1800PX的手机

可以看到适配效果很好,几乎没有无法接受的地方 代码如下:

复制代码
public class MainActivity extends AppCompatActivity {    private TextView tv;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        tv = (TextView) findViewById(R.id.tv);        tv.setText(getScreenParams());    }    private String getScreenParams() {        DisplayMetrics dm = new DisplayMetrics();        getWindowManager().getDefaultDisplay().getMetrics(dm);        int heightPixels = dm.heightPixels;//高的像素        int widthPixels = dm.widthPixels;//宽的像素        int densityDpi = dm.densityDpi;//dpi        float xdpi = dm.xdpi;//xdpi        float ydpi = dm.ydpi;//ydpi        float density = dm.density;//density=dpi/160,密度比        float scaledDensity = dm.scaledDensity;//scaledDensity=dpi/160 字体缩放密度比        float heightDP = heightPixels / density;//高度的dp        float widthDP = widthPixels / density;//宽度的dp        String str = "heightPixels: " + heightPixels + "px";        str += "\nwidthPixels: " + widthPixels + "px";        str += "\ndensityDpi: " + densityDpi + "dpi";        str += "\nxdpi: " + xdpi + "dpi";        str += "\nydpi: " + ydpi + "dpi";        str += "\ndensity: " + density;        str += "\nscaledDensity: " + scaledDensity;        str += "\nheightDP: " + heightDP + "dp";        str += "\nwidthDP: " + widthDP + "dp";        return str;    }}复制代码

**dp适配优点:**dimens文件很少,不影响apk包的体积, 缺点:在每次打包(如果需要调试sw360dp之外的手机,或者上线APP)之前,都需要执行DPGeneratorLittle的main方法,以确保所有的dimen文件都是最新的,有点烦躁

想不烦躁可以,你可以用的DPGenerator类 直接生成所有可能用到的dp,sp,但是这样,可能会增加好几百KB文件

各位老铁有问题欢迎及时联系、指正、批评、撕逼

关注专题

微信公众号

QQ群

转载于:https://juejin.im/post/5bd26d236fb9a05d3a4b7b57

你可能感兴趣的文章
Jstl 中<c:if test="${value=='0'}"> 不能做判断??
查看>>
python matplotlib及sklearn安装
查看>>
困惑2017?
查看>>
KOTree
查看>>
BlockAlertsAnd-ActionSheets
查看>>
开源 java CMS - FreeCMS2.5 标签formTable自定义表单
查看>>
FreeCMS视频教程 将FreeCMS导入myeclipse
查看>>
Android 8.0 SystemUI(一):图文并茂的介绍 :D
查看>>
1wifi 简介(框架)
查看>>
internet && intranet
查看>>
go get报错 error: RPC failed; result=56, HTTP code =
查看>>
串行(Sequential)、并发(Concurrent)、并行(parallel)与分布式
查看>>
JAVA NIO学习笔记之Channel(基础篇)
查看>>
Xcode升级到6.4之后插件无法使用,重新安装最新也无法使用的解决办法
查看>>
秒懂科技新概念
查看>>
eclipse启动tomcat无法访问
查看>>
Notepad++ 书签
查看>>
TiDB 集群测试
查看>>
十天学会php之第五天
查看>>
Java基础10
查看>>