鸿神的AutoLayout堪称适配终结者,虽然项目已经不再维护,对于一些好的框架和实现原理我们还是有学习意义的!现在让我们一起来学习AutoLayout的源码吧!

首先我们应该思考这样一个问题:对于一个框架如何去学习呢?

难道是下载源码后,一个类一个类的去看吗? 这肯定不行,这样会浪费大量的时间,而且还不一定能很好的理解他

正确的流程应该是从如何去使用入手,然后根据调用流程一步一步的去理解,这样就能很好的理解框架的整个流程了!

现在让我们来看一下AutoLayout的使用流程:

1、将Autolayout添加到我们的项目依赖中

dependencies {// 其他依赖// ...implementation 'com.zhy:autolayout:1.4.5'
}

2、在AndroidManifest.xml标明设计稿尺寸

<!--ui设计稿提供的手机尺寸-->
<meta-data android:name="design_width" android:value="768"/>
<meta-data android:name="design_height" android:value="1280"/>

3、在Application中进行初始化

AutoLayoutConifg.getInstance().useDeviceSize().init(this);

4、让你的Activity继承AutoLayoutActivity或者直接在xml使用AutoLinearLayout、AutoRelativeLayout、AutoFrameLayout

首先出现在我们眼前的有AutoLayoutConifg、AutoLayoutActivity、AutoLinearLayout、AutoRelativeLayout、AutoFrameLayout这些类

那我们就按顺序分别去看这些类做了那些事情

1、AutoLayoutConifg

首先这是一个单例类

private static AutoLayoutConifg sIntance = new AutoLayoutConifg();private AutoLayoutConifg()
{
}public static AutoLayoutConifg getInstance()
{return sIntance;
}

它有这些属性

// 屏幕的宽度
private int mScreenWidth;
// 屏幕的高度
private int mScreenHeight;
// 设计图的宽度
private int mDesignWidth;
// 设计图的高度
private int mDesignHeight;
// 暂时不知道这个参数是啥,字面意思是使用设备大小
private boolean useDeviceSize;

接下来我们来看init里面做了啥

public void init(Context context)
{// 通过方法名我们猜想应该是从AndroidManifest读取MetaData里面的设计尺寸getMetaData(context);// 这里是获取实际屏幕的大小,传入了useDeviceSize属性   int[] screenSize = ScreenUtils.getScreenSize(context, useDeviceSize);// 给mScreenWidth赋值mScreenWidth = screenSize[0];// 给mScreenHeight赋值mScreenHeight = screenSize[1];L.e(" screenWidth =" + mScreenWidth + " ,screenHeight = " + mScreenHeight);
}

接下来我们来看getMetaData方法里的代码

private void getMetaData(Context context)
{PackageManager packageManager = context.getPackageManager();ApplicationInfo applicationInfo;try{applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);if (applicationInfo != null && applicationInfo.metaData != null){// 给mDesignWidth赋值mDesignWidth = (int) applicationInfo.metaData.get(KEY_DESIGN_WIDTH);// 给mDesignHeight赋值mDesignHeight = (int) applicationInfo.metaData.get(KEY_DESIGN_HEIGHT);}} catch (PackageManager.NameNotFoundException e){throw new RuntimeException("you must set " + KEY_DESIGN_WIDTH + " and " + KEY_DESIGN_HEIGHT + "  in your manifest file.", e);}L.e(" designWidth =" + mDesignWidth + " , designHeight = " + mDesignHeight);
}

这里印证了我们的猜想:这个方法就是从AndroidManifest读取MetaData里面的设计尺寸,然后赋值给属性[mDesignWidth、mDesignHeight]

接下来我们看 ScreenUtils.getScreenSize(context, useDeviceSize)方法是如何获取真实的屏幕大小,顺便确定useDeviceSize属性的意思

   
        int[] size = new int[2];// 这部分的代码是获取屏幕宽高的代码,有多种写法,写法也比较固定WindowManager w = (WindowManager)         context.getSystemService(Context.WINDOW_SERVICE);Display d = w.getDefaultDisplay();DisplayMetrics metrics = new DisplayMetrics();d.getMetrics(metrics);int widthPixels = metrics.widthPixels;int heightPixels = metrics.heightPixels;// since SDK_INT = 1;if (!useDeviceSize){size[0] = widthPixels;size[1] = heightPixels - getStatusBarHeight(context);return size;}

我们看红色注释部分,可以看到从SDK1开始,只有useDeviceSize为false才会走进来

      
  // includes window decorations (statusbar bar/menu bar)if (Build.VERSION.SDK_INT >= 14 && Build.VERSION.SDK_INT < 17)try{widthPixels = (Integer) Display.class.getMethod("getRawWidth").invoke(d);heightPixels = (Integer) Display.class.getMethod("getRawHeight").invoke(d);} catch (Exception ignored){}// includes window decorations (statusbar bar/menu bar)if (Build.VERSION.SDK_INT >= 17)try{Point realSize = new Point();Display.class.getMethod("getRealSize", Point.class).invoke(d,realSize);widthPixels = realSize.x;heightPixels = realSize.y;} catch (Exception ignored){}size[0] = widthPixels;size[1] = heightPixels;return size;

可以看到接下来部分的代码都不同版本的适配代码,所以我们可以这样理解useDeviceSize:根据版本来适配获取屏幕真实宽高的,所以当我们项目的最低版本大于等于14时,应该在Application里面去将useDeviceSize配置为true。因为现在构建工程的最低版本基本都是19或21了,所以应该将useDeviceSize设置为true。

那么如果我们不设置会出现什么问题呢?

通过DisplayMetrics获取到的heightPixels的值在高版本中是已经减去了状态栏高度的,而代码里面又减了一次状态栏高度,会导致减去了2次状态栏的高度,从而导致适配的缩放比例存在误差

其实看完这个类如果我们善于思考的话,已经能够猜到它的大致原理了

比如设计图上有一个384px*640px的View,设计图是768x1280,我们的手机分辨率是1440x3168,所以:

mScreenWidth = 1440;
mScreenHeight = 3168;
mDesignWidth = 768;
mDesignHeight = 1280;

可以看到设计图上这个View占用了屏幕的4分之1,我们要在1440x3168的屏幕上也显示4分之1,怎么办呢?

我们用1440*384/760 = 720,3168*640/1280 = 1584,所以我们想要在1440x3168的屏幕上显示相同的效果这个控件的大小应该是720px*1584px,目前大致能想到是就这么多了,接下来我们继续看源码是如何实现的!

AutoLayout源码解析(1)相关推荐

  1. 谷歌BERT预训练源码解析(二):模型构建

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/weixin_39470744/arti ...

  2. 谷歌BERT预训练源码解析(三):训练过程

    目录 前言 源码解析 主函数 自定义模型 遮蔽词预测 下一句预测 规范化数据集 前言 本部分介绍BERT训练过程,BERT模型训练过程是在自己的TPU上进行的,这部分我没做过研究所以不做深入探讨.BE ...

  3. 谷歌BERT预训练源码解析(一):训练数据生成

    目录 预训练源码结构简介 输入输出 源码解析 参数 主函数 创建训练实例 下一句预测&实例生成 随机遮蔽 输出 结果一览 预训练源码结构简介 关于BERT,简单来说,它是一个基于Transfo ...

  4. Gin源码解析和例子——中间件(middleware)

    在<Gin源码解析和例子--路由>一文中,我们已经初识中间件.本文将继续探讨这个技术.(转载请指明出于breaksoftware的csdn博客) Gin的中间件,本质是一个匿名回调函数.这 ...

  5. Colly源码解析——结合例子分析底层实现

    通过<Colly源码解析--框架>分析,我们可以知道Colly执行的主要流程.本文将结合http://go-colly.org上的例子分析一些高级设置的底层实现.(转载请指明出于break ...

  6. libev源码解析——定时器监视器和组织形式

    我们先看下定时器监视器的数据结构.(转载请指明出于breaksoftware的csdn博客) /* invoked after a specific time, repeatable (based o ...

  7. libev源码解析——定时器原理

    本文将回答<libev源码解析--I/O模型>中抛出的两个问题.(转载请指明出于breaksoftware的csdn博客) 对于问题1:为什么backend_poll函数需要指定超时?我们 ...

  8. libev源码解析——I/O模型

    在<libev源码解析--总览>一文中,我们介绍过,libev是一个基于事件的循环库.本文将介绍其和事件及循环之间的关系.(转载请指明出于breaksoftware的csdn博客) 目前i ...

  9. libev源码解析——调度策略

    在<libev源码解析--监视器(watcher)结构和组织形式>中介绍过,监视器分为[2,-2]区间5个等级的优先级.等级为2的监视器最高优,然后依次递减.不区分监视器类型和关联的文件描 ...

  10. libev源码解析——监视器(watcher)结构和组织形式

    在<libev源码解析--总览>中,我们介绍了libev的一些重要变量在不同编译参数下的定义位置.由于这些变量在多线程下没有同步问题,所以我们将问题简化,所提到的变量都是线程内部独有的,不 ...

最新文章

  1. 【唠叨两句】如何将一张树型结构的Excel表格中的数据导入到多张数据库表中...
  2. 为什么现在改用int.TryParse了
  3. php call_user_func_array 性能,php-call_user_func_array是否太慢?
  4. 一个地址或二维码自动识别设备,并跳转到各自相应的下载地址,兼容微信
  5. SpringBoot(六)_AOP统一处理请求
  6. mysql类型设计_mysql设计表结构数据类型的选择
  7. GCD LCM UVA - 11388 (思维。。水题)
  8. 用python做mud
  9. Win7 路由上网DNS服务器ping不通的解决方法
  10. lodop打印html内容,Lodop打印控件在页面如何使用
  11. Java 7:项目代币中的代码示例
  12. CORS error 状态码451
  13. Python笔记-函数装饰器的缺点
  14. [Angularjs]angular ng-repeat与js特效加载先后导致的问题
  15. 4月第3周业务风控关注 | 文化部再次审查直播和游戏产品,已下架4939款直播应用...
  16. ajax同步导致ajax上面的代码不执行?
  17. winform定义数据源名称_WinForm中使用CrystalReport水晶报表——基础,分组统计,自定义数据源...
  18. model 字段参数 choice
  19. MiPony– 杀手级免费网盘下载工具 可挂机下载支持YunFile
  20. 用PPT就可以做印章?是的,超简单超逼真,教你一分钟搞定

热门文章

  1. Directx使用基础
  2. 第2章 大数据处理架构Hadoop
  3. 跨平台的音视频即时通讯SDK
  4. 【联合路由频谱分配】多跳认知无线电网络中的联合路由和动态频谱分配的matlab仿真与分析
  5. android中edittext监听文字变化,使用TextWatcher监听EditText变化
  6. 实用干货秘籍!最经典的10个Pandas数据查询案例,收藏!
  7. HDU操作系统课程设计实验一
  8. 摩尔定律终结后 科技也许会向这3个方向前进
  9. 全国计算机二级公共基础知识电子版,全国计算机二级公共基础知识汇总.pdf
  10. Excel任务该如何在FineReader 12中设置