结构
我在上面就已经说了很多开源框架以及解决方案,只是把 今日头条屏幕适配方案 简单的封装成一个工具类然后引入项目,这时很多人就会说了 今日头条屏幕适配方案 官方公布的全部代码都只有 30 行不到,你不把它封装成工具类,那封装成什么?该怎么封装?下面就来看看 AndroidAutoSize 的整体结构
├── external│ ├── ExternalAdaptInfo.java│ ├── ExternalAdaptManager.java│── internal│ ├── CancelAdapt.java│ ├── CustomAdapt.java│── unit│ ├── Subunits.java│ ├── UnitsManager.java│── utils│ ├── AutoSizeUtils.java│ ├── LogUtils.java│ ├── Preconditions.java│ ├── ScreenUtils.java├── ActivityLifecycleCallbacksImpl.java├── AutoAdaptStrategy.java├── AutoSize.java├── AutoSizeConfig.java├── DefaultAutoAdaptStrategy.java├── DisplayMetricsInfo.java├── FragmentLifecycleCallbacksImpl.java├── InitProvider.java
AndroidAutoSize 根据 今日头条屏幕适配方案 官方公布的 30 行不到的代码,经过不断的优化和扩展,发展成了现在拥有 18 个类文件,上千行代码的全面性屏幕适配框架,在迭代的过程中完善和优化了很多功能,相比 今日头条屏幕适配方案 官方公布的原始代码,AndroidAutoSize 更加稳定、更加易用、更加强大,欢迎阅读源码,注释非常详细哦!
功能介绍
AndroidAutoSize 在使用上非常简单,只需要填写设计图尺寸这一步即可接入项目,但需要注意的是,AndroidAutoSize 有两种类型的布局单位可以选择,一个是 主单位 (dp、sp),一个是 副单位 (pt、in、mm),两种单位面向的应用场景都有不同,也都有各自的优缺点
主单位: 使用 dp、sp 为单位进行布局,侵入性最低,会影响其他三方库页面、三方库控件以及系统控件的布局效果,但 AndroidAutoSize 也通过这个特性,使用 ExternalAdaptManager 实现了在不修改三方库源码的情况下适配三方库的功能
副单位: 使用 pt、in、mm 为单位进行布局,侵入性高,对老项目的支持比较好,不会影响其他三方库页面、三方库控件以及系统控件的布局效果,可以彻底的屏蔽修改 density 所造成的所有未知和已知问题,但这样 AndroidAutoSize 也就无法对三方库进行适配
大家可以根据自己的应用场景在 主单位 和 副单位 中选择一个作为布局单位,建议想引入老项目并且注重稳定性的人群使用 副单位,只是想试试本框架,随时可能切换为其他屏幕适配方案的人群使用 主单位
其实 AndroidAutoSize 可以同时支持 主单位 和 副单位,但 AndroidAutoSize 可以同时支持 主单位 和 副单位 的目的,只是为了让使用者可以在 主单位 和 副单位 之间灵活切换,因为切换单位的工作量可能非常巨大,不能立即完成,但领导又要求马上打包上线,这时就可以起到一个很好的过渡作用
主单位
主单位 的 Demo 在 demo
基本使用
将 AndroidAutoSize 引入项目后,只要在 app 的 AndroidManifest.xml 中填写上设计图尺寸,无需其他过多配置 (如果你没有其他自定义需求的话),AndroidAutoSize 即可自动运行,像下面这样
<manifest> <application> <meta-data android:name="design_width_in_dp" android:value="360"/> <meta-data android:name="design_height_in_dp" android:value="640"/> </application> </manifest>
在使用主单位时,design_width_in_dp
和 design_height_in_dp
的单位必须是 dp,如果设计师给你的设计图,只标注了 px 尺寸 (现在已经有很多 UI 工具可以自动标注 dp 尺寸了),那请自行根据公式 dp = px / (DPI / 160) 将 px 尺寸转换为 dp 尺寸,如果你不知道 DPI 是多少?那请以自己测试机的 DPI 为准,如果连怎么得到设备的 DPI 都不知道?百度吧好伐,如果你实在找不到设备的 DPI 那就直接将 px 尺寸除以 3 或者 2 也是可以的
如果你只是想使用 AndroidAutoSize 的基础功能,AndroidAutoSize 的使用方法在这里就结束了,只需要上面这一步,即可帮助你以最简单的方式接入 AndroidAutoSize,但是作为一个全面性的屏幕适配框架,在保证基础功能的简易性的同时,也必须保证复杂的需求也能在框架内被解决,从而达到一个小闭环,所以下面介绍的内容全是前人踩坑踩出来的一些必备功能,如果你没这个需求,或者觉得麻烦,可以按需查看或者跳过,下面的内容建议和 Demo 配合起来阅读,效果更佳
注意事项
你在 AndroidManifest.xml 中怎么把设计图的 px 尺寸转换为 dp 尺寸,那在布局时,每个控件的大小也需要以同样的方式将设计图上标注的 px 尺寸转换为 dp 尺寸,千万不要在 AndroidManifest.xml 中填写的是 dp 尺寸,却在布局中继续填写设计图上标注的 px 尺寸
design_width_in_dp
和design_height_in_dp
虽然都需要填写,但是 AndroidAutoSize 只会将高度和宽度其中的一个作为基准进行适配,一方作为基准,另一方就会变为备用,默认以宽度为基准进行适配,可以通过 AutoSizeConfig#setBaseOnWidth(Boolean) 不停的切换,这意味着最后运行到设备上的布局效果,在高度和宽度中只有一方可以和设计图上一模一样,另外一方会和设计图出现偏差,为什么不像 AndroidAutoLayout 一样,高和宽都以设计图的效果等比例完美呈现呢?这也很简单,你无法保证所有设备的高宽比例都和你设计图上的高宽比例一致,特别是在现在全面屏全面推出的情况下,如果这里不这样做的话,当你的项目运行在与设计图高宽比例不一致的设备上时,布局会出现严重的变形,这个几率非常大,详情请看 这里
自动运行是如何做到的?
很多人有疑惑,为什么使用者只需要在 AndroidManifest.xml 中填写一下 meta-data 标签,其他什么都不做,AndroidAutoSize 就能自动运行,并在 App 启动时自动解析 AndroidManifest.xml 中填写的设计图尺寸,这里很多人不敢相信,问我真的只需要填写下设计图尺寸框架就可以正常运行吗?难道使用了什么 黑科技?
其实这里并没有用到什么 黑科技,原理反而非常简单,只需要声明一个 ContentProvider,在它的 onCreate 方法中启动框架即可,在 App 启动时,系统会在 App 的主进程中自动实例化你声明的这个 ContentProvider,并调用它的 onCreate 方法,执行时机比 Application#onCreate 还靠前,可以做一些初始化的工作,get 到了吗?
这里需要注意的是,如果你的项目拥有多进程,系统只会在主进程中实例化一个你声明的 ContentProvider,并不会在其他非主进程中实例化 ContentProvider,如果在当前进程中 ContentProvider 没有被实例化,那 ContentProvider#onCreate 就不会被调用,你的初始化代码在当前进程中也就不会执行,这时就需要在 Application#onCreate 中调用下 ContentProvider#query 执行一下查询操作,这时 ContentProvider 就会在当前进程中实例化 (每个进程中只会保证有一个实例),所以应用到框架中就是,如果你需要在多个进程中都进行屏幕适配,那就需要在 Application#onCreate 中调用 AutoSize#initCompatMultiProcess 方法
进阶使用
虽然 AndroidAutoSize 不需要其他过多的配置,只需要在 AndroidManifest.xml 中填写下设计图尺寸就能正常运行,但 AndroidAutoSize 还是为大家准备了很多可配置选项,尽最大可能满足大家日常开发中的所有扩展需求
所有的全局配置选项在 Demo 中都有介绍,每个 API 中也都有详细的注释,在这里就不过多介绍了
自定义 Activity
在 AndroidManifest.xml 中填写的设计图尺寸,是整个项目的全局设计图尺寸,但是如果某些 Activity 页面由于某些原因,设计师单独出图,这个页面的设计图尺寸和在 AndroidManifest.xml 中填写的设计图尺寸不一样该怎么办呢?不要急,AndroidAutoSize 已经为你考虑好了,让这个页面的 Activity 实现 CustomAdapt 接口即可实现你的需求,CustomAdapt 接口的第一个方法可以修改当前页面的设计图尺寸,第二个方法可以切换当前页面的适配基准,下面的注释都解释的很清楚
public class CustomAdaptActivity extends AppCompatActivity implements CustomAdapt { /** * 是否按照宽度进行等比例适配 (为了保证在高宽比不同的屏幕上也能正常适配, 所以只能在宽度和高度之中选择一个作为基准进行适配) * * @return {@code true} 为按照宽度进行适配, {@code false} 为按照高度进行适配 */ @Override public boolean isBaseOnWidth() { return false; } /** * 这里使用 iPhone 的设计图, iPhone 的设计图尺寸为 750px * 1334px, 高换算成 dp 为 667 (1334px / 2 = 667dp) * <p> * 返回设计图上的设计尺寸, 单位 dp * {@link #getSizeInDp} 须配合 {@link #isBaseOnWidth()} 使用, 规则如下: * 如果 {@link #isBaseOnWidth()} 返回 {@code true}, {@link #getSizeInDp} 则应该返回设计图的总宽度 * 如果 {@link #isBaseOnWidth()} 返回 {@code false}, {@link #getSizeInDp} 则应该返回设计图的总高度 * 如果您不需要自定义设计图上的设计尺寸, 想继续使用在 AndroidManifest 中填写的设计图尺寸, {@link #getSizeInDp} 则返回 {@code 0} * * @return 设计图上的设计尺寸, 单位 dp */ @Override public float getSizeInDp() { return 667; } }
如果某个 Activity 想放弃适配,让这个 Activity 实现 CancelAdapt 接口即可,比如修改 density 影响到了老项目中的某些 Activity 页面的布局效果,这时就可以让这个 Activity 实现 CancelAdapt 接口
public class CancelAdaptActivity extends AppCompatActivity implements CancelAdapt { }
自定义 Fragment
Fragment 的自定义方式和 Activity 是一样的,只不过在使用前需要先在 App 初始化时开启对 Fragment 的支持
AutoSizeConfig.getInstance().setCustomFragment(true);
实现 CustomAdapt
public class CustomAdaptFragment extends Fragment implements CustomAdapt { @Override public boolean isBaseOnWidth() { return false; } @Override public float getSizeInDp() { return 667; } }
实现 CancelAdapt
public class CancelAdaptFragment extends Fragment implements CancelAdapt { }
适配三方库页面
在使用主单位时可以使用 ExternalAdaptManager 来实现在不修改三方库源码的情况下,适配三方库的所有页面 (Activity、Fragment)
由于 AndroidAutoSize 要求需要自定义适配参数或取消适配的页面必须实现 CustomAdapt、CancelAdapt,这时问题就来了,三方库是通过远程依赖的,我们无法修改它的源码,这时我们怎么让三方库的页面也能实现自定义适配参数或取消适配呢?别急,这个需求 AndroidAutoSize 也已经为你考虑好了,当然不会让你将三方库下载到本地然后改源码!
通过 ExternalAdaptManager#addExternalAdaptInfoOfActivity(Class, ExternalAdaptInfo) 将需要自定义的类和自定义适配参数添加进方法即可替代实现 CustomAdapt 的方式,这里 展示了使用方式,以及详细的注释
通过 ExternalAdaptManager#addCancelAdaptOfActivity(Class) 将需要取消适配的类添加进方法即可替代实现 CancelAdapt 的方式,这里 也展示了使用方式,以及详细的注释
需要注意的是 ExternalAdaptManager 的方法虽然可以添加任何类,但是只能支持 Activity、Fragment,并且 ExternalAdaptManager 是支持链式调用的,以便于持续添加多个页面
当然 ExternalAdaptManager 不仅可以对三方库的页面使用,也可以让自己项目中的 Activity、Fragment 不用实现 CustomAdapt、CancelAdapt 即可达到自定义适配参数和取消适配的功能
副单位
前面已经介绍了 副单位 的应用场景,这里就直接介绍 副单位 如何使用,副单位 的 Demo 在 demo-subunits
基本使用
首先和 主单位 一样也需要先在 app 的 AndroidManifest.xml 中填写上设计图尺寸,但和 主单位 不一样的是,当在使用 副单位 时 design_width_in_dp
和 design_height_in_dp
的单位不需要一定是 dp,可以直接填写设计图的 px 尺寸,在布局文件中每个控件的大小也可以直接填写设计图上标注的 px 尺寸,无需再将 px 转换为 dp,这是 副单位的 特性之一,可以帮助大家提高开发效率
<manifest> <application> <meta-data android:name="design_width_in_dp" android:value="1080"/> <meta-data android:name="design_height_in_dp" android:value="1920"/> </application> </manifest>
由于 AndroidAutoSize 提供了 pt、in、mm 三种类型的 副单位 供使用者选择,所以在使用 副单位 时,还需要在 APP 初始化时,通过 UnitsManager#setSupportSubunits(Subunits) 方法选择一个你喜欢的副单位,然后在布局文件中使用这个副单位进行布局,三种类型的副单位,其实效果都是一样,大家按喜欢的名字选择即可
由于使用副单位是为了彻底屏蔽修改 density 所造成的对三方库页面、三方库控件以及系统控件的布局效果的影响,所以在使用副单位时建议调用 UnitsManager#setSupportDP(false) 和 UnitsManager#setSupportSP(false),关闭 AndroidAutoSize 对 dp 和 sp 的支持,AndroidAutoSize 为什么不在使用 副单位 时默认关闭对 dp、sp 的支持?因为允许同时支持 主单位 和 副单位 可以帮助使用者在 主单位 和 副单位 之间切换时更好的过渡,这点在前面就已经提到过
UnitsManager 的详细使用方法,在 demo-subunits 中都有展示,注释也十分详细
自定义 Activity 和 Fragment
在使用 副单位 时自定义 Activity 和 Fragment 的方式是和 主单位 是一样的,这里就不再过多介绍了
适配三方库页面
如果你的项目在使用 副单位 并且关闭了对 主单位 (dp、sp) 的支持,这时 ExternalAdaptManager 对三方库的页面是不起作用的,只对自己项目中的页面起作用,除非三方库的页面也使用了副单位 (pt、in、mm) 进行布局
其实 副单位 之所以能彻底屏蔽修改 density 所造成的对三方库页面、三方库控件以及系统控件的布局效果的影响,就是因为三方库页面、三方库控件以及系统控件基本上使用的都是 dp、sp 进行布局,所以只要 AndroidAutoSize 关闭了对 dp、sp 的支持,转而使用 副单位 进行布局,就能彻底屏蔽修改 density 所造成的对三方库页面、三方库控件以及系统控件的布局效果的影响
但这也同样意味着使用 副单位 就不能适配三方库的页面了,ExternalAdaptManager 也就对三方库的页面不起作用了
布局实时预览
在开发阶段布局时的实时预览是一个很重要的环节,很多情况下 Android Studio 提供的默认预览设备并不能完全展示我们的设计图,所以我们就需要自己创建模拟设备,dp、pt、in、mm 这四种单位的模拟设备创建方法请看 这里
总结
AndroidAutoSize 在经历了 240+ commit、60+ issues、6 个版本 的洗礼后,逐渐的稳定了下来,已经在上个星期发布了首个正式版,在这里要感谢将 AndroidAutoSize 接入到自己项目中的上千个使用者,感谢他们的信赖,AndroidAutoSize 创建的初衷就是为了让所有使用 今日头条屏幕适配方案 的使用者能有一个可以一起交流、沟通的聚集地,所以后面也会持续的收集并解决 今日头条屏幕适配方案的常见问题,让 今日头条屏幕适配方案 变得更加成熟、稳定
作者:JessYan
链接:https://www.jianshu.com/p/4aa23d69d481