android 中的屏幕适配是一个老生常谈的问题,也是一个经久不衰的话题,同时随着android 生态的不断发展,适配方案也不断进行了调整,今天我们就来聊一聊android中的适配到底有哪些奥秘。
为什么要进行屏幕适配
Android系统经过十几年的发展,已然成为主流系统之一,同时作为开源系统,其允许各个手机厂商按照自己的喜好去定制系统,因此在此基础上出现了各个手机厂商的专属系统,比如:小米的MIUI系统,华为的EMUI系统,OPPO的ColorOS系统,VIVO的Funtouch OS系统,众多的系统加之每年不断的新品推出,使android手机市场出现了一派欣欣向荣的现象,各个手机厂商不断推出新的手机品牌,各种尺寸屏幕手机,各种分辨率手机充斥在手机市场,导致android系统的手机碎片化非常严重,如下图所示:
Android 手机严重的碎片化导致了一个问题,即在开发android APP的时候,必须要考虑适配的问题,其基本的含义就是开发出来的APP能够正常的在众多的手机上面使用,不会出现错位,失真,文字忽大忽小等等的问题。
什么是屏幕适配
屏幕适配顾名思义就是针对不同的手机屏幕做合理的呈现展示,再专业一点的说法就是根据不同的手机尺寸、不同的手机屏幕分辨率去进行合理的代码编写以达到同一套代码生成的APP能够满足在众多手机上面呈现出同样的展示效果。
如何进行屏幕适配
在开始讲解如何进行屏幕适配之前,我们先要弄明白几个名词的含义。
名词一:屏幕尺寸
屏幕尺寸指的是屏幕的物理尺寸,单位为英寸,和我们熟知的物理单位换算公式为:
1英寸 = 2.54厘米。而在手机上,我们也会经常听到英寸单位,目前市面上面流行的手机尺寸5.X英寸、6.X英寸居多。同时需要说明的是手机上面所说的手机英寸是以手机的对角线来计算的。
名词二:手机分辨率(屏幕分辨率)
手机分辨率也是我们经常听到的名称,其实更专业一点的叫法是屏幕分辨率,通常情况下常见的屏幕分辨率有 320x480 480x800 720x1080 1080x1920 1080x2160 1440x2960 等等。其中第一个数字代表的是横向(宽),第二个数字代表的是纵向(高),屏幕分辨率的基本单位是像素,即PX。假设现在有一个手机的屏幕分辨率为1080*1920,即意味着在这个手机横向上面有1080个像素点,在纵向上有1920个像素点,注意,像素是矢量单位。
名词三:dp/dip(像素密度)
这是android官方建议使用的单位,使用此单位可以在一定程度上满足众多机型适配的需要,既然它有如此重要的能力,那么我们首先就得了解dp到底是怎么一回事。dp/dip中文名为像素密度,它与手机的屏幕尺寸和屏幕分辨率没有直接的关系,但是却有着间接关系。讲解dp之前,我们需要知道另外一个单位PPI。关于PPI,这里就不展开讲解,感兴趣的朋友可以去看看我上一篇博客。PPI的计算公式为:
而这个公式里面的长度/宽度像素数对应的就是我们的屏幕分辨率,对角线英寸数对应的就是我们的屏幕尺寸,通过这个公式我们就能很轻易的算出ppi。那么ppi和dp又是什么关系呢?其实dp是Android基于物理设备的ppi抽象出来的一个单位。它是以160dpi的屏幕为基准定义的,在160dpi的屏幕的屏幕上1dp=1px,那么我们就可以得出其换算公式:
1dp=(屏幕ppi/ 160)px
现在我们举个例子来验证一下:
手机型号(Redmi K30 5G):屏幕分辨率 10802400 6.67英寸。对应结果的ppi为 ppi = 395,最终1dp = 2.5px。现在有一个100dp的位图,占据整个横向的比例为:1002.5/1080*100%=23%
手机型号(vivo Y3s):屏幕分辨率 7201544 6.35英寸。对应结果的ppi为 ppi = 268,最终1dp = 1.7px。现在有一个100dp的位图,占据整个横向的比例为:1001.7/720*100%=23%
注意,所有的取值都是取整
看见没有,两个完全不同的手机,不同的屏幕分辨率,不同的尺寸,但是最终相同dp的位图占据的百分比却是一样的,这就是dp的魅力。
名词四:density(1dp占据当前设备多少像素)
根据前面dp的讲解,其实我们不难推断出:
density = ppi/160。由于手机屏幕分辨率与手机尺寸的不同,导致每个手机的density的最终结果值也不一样。而由于我们采用的都是dp作为单位,所以我们得知道我们手机屏幕的总dp是多少,总dp的计算公式为:
手机屏幕的总dp = 屏幕的总px / density。再回到上面的例子,同样分辨率但是不同尺寸的手机最终的density不同,而因为屏幕的总px相同,所以导致手机的总dp不同,于是就会出现屏幕适配问题。既然我们想保证手机屏幕的总dp不变,而屏幕的总px是可变的,那么我们就只能动态去改变density的值,而不能按照ppi/160的值来计算density。
介绍完了几个比较重要的名词,下面我们就开始进入正题了,屏幕适配的方案有很多种,我选取了几种目前比较流行的来讲解,这几种没有好坏之分,需要根据自己的情况选择,毕竟,适合自己的才是最好的。
屏幕适配方案一:采用系统推荐的dp单位+自适应布局+采用weight
这是官方推荐的适配方案,为什么采用dp,前面已经讲到了,这里就不再赘述了。那么,什么是自适应布局,什么又是weight呢?我们都知道,android有五大布局,即
·RelativeLayout(相对布局)
·LinearLayout(线性布局)
·AbsoluteLayout(绝对布局):被谷歌废弃
·FrameLayout(帧布局)
·TabLayout(表格布局):被GridView代替
关于五大布局的使用不是我们的重点,其中的AbsoluteLayout(绝对布局)和TabLayout(表格布局)由于一些缺陷已经被弃用,我们经常使用的是RelativeLayout(相对布局)和LinearLayout(线性布局),而恰恰这两种布局可以满足自适应的要求,究其原因是它们都是以一个View作为基准,在此基础上进行排列,这样很大程度上能够保证布局最终呈现出来的一致性。那么weight又是什么呢?其实可以将它理解为比重,它没有具体值,是根据比重值来动态计算最终呈现的样子,这样就能保证不论屏幕的尺寸是多少,最终展示出来的都是按照比例来设定的,举一个列子,并排排列两个button按钮,每个设置的比重都是1,也就意味着两个button平分整个宽度,不管屏幕的宽度是多少,最终两个按钮呈现出来的都是1:1的比例。假设将第一个button的weight设置为2,第二个button的weight还是1,那么总的值为3,那么系统会将整个屏幕的横向分为3份,其中第一个button占据2/3的距离,第二个占据1/3的比例。具体的效果大家可以通过代码自行去测试,这里就不展开显示了。
所以说通过dp +自适应布局+weight可以在很大程度上满足屏幕适配的需求,而这也是官方推荐的方式。那么,这种方式有没有缺陷呢?肯定是有的,还记得上面的例子吗?如果我们的手机屏幕分辨率为1080*1920,但是尺寸却是5英寸,这个时候展现出来的界面就会不一样了。
屏幕适配方案二:今日头条适配方案
知道了采用dp作为尺寸的不足之处后,我们就来想想有没有办法可以解决这个问题呢?
这个里面就要涉及到另外一个名词了,即density,由上面的讲解可知,如果我们想保证手机屏幕的总dp不变,而屏幕的总px是可变的,那么我们就只能动态去改变density的值,而不能按照ppi/160的值来计算density。那么如何去动态改变density的值而保证总的dp不变呢?我们首先需要看看系统是怎么计算density的。
在源码TypedValue.java类中有这么几行代码
public static float applyDimension(int unit, float value,
DisplayMetrics metrics)
{
switch (unit) {
case COMPLEX_UNIT_PX:
return value;
case COMPLEX_UNIT_DIP:
return value * metrics.density;
case COMPLEX_UNIT_SP:
return value * metrics.scaledDensity;
case COMPLEX_UNIT_PT:
return value * metrics.xdpi * (1.0f/72);
case COMPLEX_UNIT_IN:
return value * metrics.xdpi;
case COMPLEX_UNIT_MM:
return value * metrics.xdpi * (1.0f/25.4f);
}
return 0;
}
其主要的作用就是将其他尺寸单位(例如dp,sp)转换为像素单位px。现在我们来测试一下,举个例子:
例1:设计图总宽度为375dp,屏幕宽度为1080px,可以得出density,1080/375=2.88。假如一个view为60dp,60dp算成px就是 60dp*2.88=172.8px,所占屏幕宽度比为 172.8/1080=0.16
例2:设计图总宽度为375dp,屏幕宽度为1440px,可以得出density,1440/375=3.84。假如一个view为60dp,60dp那么算成px就是 60dp*3.84=230.4px,所占屏幕宽度比为 172.8/1440=0.16。
由示例1,2可以看出,虽然屏幕宽度不一样,但是都实现了屏幕等比例适配。
那么具体的代码该怎么写呢?其实也很简单:
/**
* author: zhoufan
* data: 2021/6/24 14:35
* content: 采用今日头条的理念进行屏幕适配
*/
public class ScreenAdaptationUtil {
private static float sComponentDensity;
private static float sComponentScaleDensity;
public static void setCustomDensity(Activity activity, Application application, int defaultDP) {
// 获取测量屏幕尺寸的DisplayMetrics
final DisplayMetrics appDisplayMetrics = application.getResources().getDisplayMetrics();
if (sComponentDensity == 0) {
sComponentDensity = appDisplayMetrics.density;
sComponentScaleDensity = appDisplayMetrics.scaledDensity;
application.registerComponentCallbacks(new ComponentCallbacks() {
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
if (newConfig.fontScale > 0) {
sComponentScaleDensity = application.getResources().getDisplayMetrics().scaledDensity;
}
}
@Override
public void onLowMemory() {
}
});
}
// 获取屏幕的总px,然后除以我们美工设计的总dp,得到我们的目标density
final float targetDensity = appDisplayMetrics.widthPixels / defaultDP;
final float targetScaledDensity = targetDensity * (sComponentScaleDensity / sComponentDensity);
// 得到我们的dpi
final int targetDensityDpi = (int) (160 * targetDensity);
// 重新设置density和densityDpi
appDisplayMetrics.density = targetDensity;
appDisplayMetrics.scaledDensity = targetScaledDensity;
appDisplayMetrics.densityDpi = targetDensityDpi;
final DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();
activityDisplayMetrics.density = targetDensity;
appDisplayMetrics.scaledDensity = targetScaledDensity;
activityDisplayMetrics.densityDpi = targetDensityDpi;
}
}
最后记得在我们的Application里面去配置就好了。
优点:
(1)使用成本非常低,操作非常简单,使用该方案后在页面布局时不需要额外的代码和操作,这点可以说完虐其他屏幕适配方案。
(2)侵入性非常低,该方案和项目完全解耦,在项目布局时不会依赖哪怕一行该方案的代码,而且使用的还是 Android官方的 API,意味着当你遇到什么问题无法解决,想切换为其他屏幕适配方案时,基本不需要更改之前的代码,整个切换过程几乎在瞬间完成,会少很多麻烦,节约很多时间。
(3)可适配三方库的控件和系统的控件(不止是 Activity和 Fragment,Dialog、Toast等所有系统控件都可以适配),由于修改的 density 在整个项目中是全局的,所以只要一次修改,项目中的所有地方都会受益。
(4)不会有任何性能的损耗。
缺点:只需要修改一次 density,项目中的所有地方都会自动适配,这个看似解放了双手,减少了很多操作,但是实际上反应了一个缺点,那就是只能一刀切的将整个项目进行适配,但适配范围是不可控的,当项目中的系统控件、三方库控件等非我们项目自身设计的控件,它们的设计图尺寸并不会和我们项目自身的设计图尺寸一样,这时就会出现问题,当某个系统控件或三方库控件的设计图尺寸和我们项目自身的设计图尺寸差距非常大时,这个问题就越严重。
但是说实话,今日头条团队提供的这个屏幕适配方案依旧非常的优秀。
屏幕适配方案三:smallestWidth限定符
不知道大家有没有用过鸿洋大神的关于屏幕适配的AndroidAutoLayout,我记得我刚开始工作的时候用的就是这个,那个时候使用这个框架会在src下面生成很多的values文件,就像这样:
├── src/main
│ ├── res
│ ├── ├──values
│ ├── ├──values-800x480
│ ├── ├──values-860x540
│ ├── ├──values-1024x600
│ ├── ├──values-1024x768
│ ├── ├──...
│ ├── ├──values-2560x1440
其实这个框架的原理也很简单,即在src下面生成多种机型的文件,在我们的APP安装到手机上运行的时候,系统会自动根据当前手机的屏幕分辨率去找到对应的values文件,从而保证在众多的机型上能够完美的适配,而我们的基准就是根据美工画图时的手机屏幕分辨率来确定。这个应该很容易理解的,至于具体的如何操作这里就不展开讲了,因为这个库作者已经停止维护了,所以我们应该去寻找更好的替代。
SmallestWidth限定符方案其实可以看作是AndroidAutoLayout的进阶版,其二者的区别有:
(1)在使用AndroidAutoLayout的使用,其对应的单位是px,而使用SmallestWidth,使用的单位则是dp。
(2)在使用AndroidAutoLayout框架的时候,如果手机系统的分辨率我们在APP里面没有包含到,会产生异常。而使用SmallestWidth,即使系统没有找到声明的文件,也会自动去寻找相近的文件从而完成适配。
原理
smallestWidth限定符屏幕适配方案的原理也很简单,甚至可以说和AndroidAutoLayout是一样的,开发者先在项目中根据主流屏幕的最小宽度生成一系列的values-sw
如何计算
如何计算就不再赘述了,其基本公式为:
总dp = 总屏幕的最小宽度px / density
density = ppi/160
至于ppi是怎么计算出来的,上面有图。
优点:
(1)非常稳定,极低概率出现意外。
(2)不会有任何性能的损耗。
(3)适配范围可自由控制,不会影响第三方库。
(4)在插件的配合下,集成非常容易,学习成本低。
缺点:
(1)由于维护的values文件非常多,因此维护麻烦
(2)侵入性高,换成其他的匹配方案修改麻烦
但是这种方案总体来说,也是众多优秀可选方案之一。
屏幕适配方案四:使用AndroidAutoSize框架
AndroidAutoSize框架是在今日头条屏幕适配方案基础上优化而来的,今日头条屏幕适配方案官方公布的代码,只实现了修改系统density的相关逻辑,但是这远远不够,而下面要讲的AndroidAutoSize框架则是在更多细致方面做更近一步的处理。
功能介绍:
AndroidAutoSize框架在使用上非常简单,接入步骤为:
(1)在build文件里面添加依赖
api 'me.jessyan:autosize:1.2.1'
(2)在清单文件里面配置属性
到这里基本的接入就结束了,这个框架在APP启动的时候就开始运作了,当然,除此之外,你还可以在Application里面做一些简单的配置。
(3)在Application里面做一些配置(可加可不加)
//当 App 中出现多进程, 并且您需要适配所有的进程, 就需要在 App 初始化时调用 initCompatMultiProcess()
AutoSize.initCompatMultiProcess(this);
//如果在某些特殊情况下出现 InitProvider 未能正常实例化, 导致 AndroidAutoSize 未能完成初始化
//可以主动调用 AutoSize.checkAndInit(this) 方法, 完成 AndroidAutoSize 的初始化后即可正常使用
//AutoSize.checkAndInit(this);
AutoSizeConfig.getInstance()
//是否让框架支持自定义 Fragment 的适配参数, 由于这个需求是比较少见的, 所以须要使用者手动开启
//如果没有这个需求建议不开启
.setCustomFragment(false)
//是否屏蔽系统字体大小对 AndroidAutoSize 的影响, 如果为 true, App 内的字体的大小将不会跟随系统设置中字体大小的改变
//如果为 false, 则会跟随系统设置中字体大小的改变, 默认为 false
.setExcludeFontScale(true)
//区别于系统字体大小的放大比例, AndroidAutoSize 允许 APP 内部可以独立于系统字体大小之外,独自拥有全局调节 APP 字体大小的能力
//当然, 在 APP 内您必须使用 sp 来作为字体的单位, 否则此功能无效, 不设置或将此值设为 0 则取消此功能
// .setPrivateFontScale(0.8f)
//屏幕适配监听器
.setOnAdaptListener(new onAdaptListener() {
@Override
public void onAdaptBefore(Object target, Activity activity) {
//使用以下代码, 可以解决横竖屏切换时的屏幕适配问题
//使用以下代码, 可支持 Android 的分屏或缩放模式, 但前提是在分屏或缩放模式下当用户改变您 App 的窗口大小时
//系统会重绘当前的页面, 经测试在某些机型, 某些情况下系统不会主动重绘当前页面, 所以这时您需要自行重绘当前页面
ScreenUtils.getScreenSize(activity);// 的参数一定要不要传 Application!!!
AutoSizeConfig.getInstance().setScreenWidth(ScreenUtils.getScreenSize(activity)[0]);
AutoSizeConfig.getInstance().setScreenHeight(ScreenUtils.getScreenSize(activity)[1]);
AutoSizeLog.d(String.format(Locale.ENGLISH, "%s onAdaptBefore!", target.getClass().getName()));
}
@Override
public void onAdaptAfter(Object target, Activity activity) {
AutoSizeLog.d(String.format(Locale.ENGLISH, "%s onAdaptAfter!", target.getClass().getName()));
}
})
//是否打印 AutoSize 的内部日志, 默认为 true, 如果您不想 AutoSize 打印日志, 则请设置为 false
.setLog(false)
//是否使用设备的实际尺寸做适配, 默认为 false, 如果设置为 false, 在以屏幕高度为基准进行适配时
//AutoSize 会将屏幕总高度减去状态栏高度来做适配
//设置为 true 则使用设备的实际屏幕高度, 不会减去状态栏高度
//在全面屏或刘海屏幕设备中, 获取到的屏幕高度可能不包含状态栏高度, 所以在全面屏设备中不需要减去状态栏高度,所以可以 setUseDeviceSize(true)
//.setUseDeviceSize(true)
//是否全局按照宽度进行等比例适配, 默认为 true, 如果设置为 false, AutoSize 会全局按照高度进行适配
//.setBaseOnWidth(false)
//设置屏幕适配逻辑策略类, 一般不用设置, 使用框架默认的就好
//.setAutoAdaptStrategy(new AutoAdaptStrategy())
;
到这里,AndroidAutoSize框架的使用就结束了,下面我们来细细看一下。在步骤2中,我们给width和height设置的单位是dp,这是android官方推荐的单位,而具体值是怎么来的呢?其实这个值是根据美工设计图的尺寸来的,注意,美工设计的图不管单位是什么,后面也须转换为dp。
关注点一:单位
AndroidAutoSize有两种类型的布局单位可以选择,一个是主单位(dp、sp),一个是副单位(pt、in、mm),两种单位各自有各自的适用场景,也有各自的优缺点。
·主单位:因为android系统本身就是使用dp和sp,所以使用dp和sp为单位进行布局对整个系统的侵入性低,但是这依然会影响其他第三方库和控件的使用。
·副单位:对系统的侵入性高,不会影响到第三方库和控件的使用。
一般情况下建议使用主单位进行适配
关注点二:width和height如何选择
虽然design_width_in_dp和design_height_in_dp都需要填写,但是在实际的适配中只会将高度和宽度当中的一个作为适配的依据,默认情况下以宽度作为适配的基准,当然也可以通过AutoSizeConfig来进行切换
AutoSizeConfig.*getInstance*().setBaseOnWidth(true);
其中true代表的是以宽度作为基准,false代表的是以高度作为基准。
关注点三:自动运行是怎么做到的
在源码里面我们找到这么一段
** // don't bring up providers in restricted mode; they may depend on the**
** // app's custom Application class**
** if (!data.restrictedBackupMode) {**
** if (!ArrayUtils.isEmpty(data.providers)) {**
** //创建ContentProvider**
** installContentProviders(app, data.providers);**
** // For process that contains content providers, we want to**
** // ensure that the JIT is enabled "at some point".**
** mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);**
** }**
** }**
** // Do this after providers, since instrumentation tests generally start their**
** // test thread at this point, and we don't want that racing.**
** try {**
** mInstrumentation.onCreate(data.instrumentationArgs);**
** }catch (Exception e) {**
** throw new RuntimeException(**
** "Exception thrown in onCreate() of "**
** + data.instrumentationName + ": " + e.toString(), e);**
** }**
** try {**
** //调用Application的OnCreate**
** mInstrumentation.callApplicationOnCreate(app);**
** } catch (Exception e) {**
** if (!mInstrumentation.onException(app, e)) {**
** throw new RuntimeException(**
** "Unable to create application " + app.getClass().getName()**
** + ": " + e.toString(), e);**
** }**
** }**
注意看:
installContentProviders(app, data.providers); // 创建contentProvider
mInstrumentation.callApplicationOnCreate(app);//调用Application的onCreate
所以说ContentProvider会在Application的onCreate之前调用,而AndroidAutoSize的原理就是只需要声明一个ContentProvider,在它的onCreate方法中启动框架即可,这里需要注意的是,如果你的项目拥有多进程,系统只会在主进程中实例化一个你声明的ContentProvider,并不会在其他的进程中实例化ContentProvider,如果在当前进程中ContentProvider没有被实例化,这时就需要在Application的onCreate方法中去查询一下,所以对于多进程的项目而言,需要在Application的onCreate中调用AutoSize的initCompatMultiProcess,如下:
//当 App 中出现多进程, 并且您需要适配所有的进程, 就需要在 App 初始化时调用 initCompatMultiProcess()
AutoSize.initCompatMultiProcess(this);
关注点四:特殊页面适配
在AndroidManifest.xml文件中填写的尺寸是属于全局的尺寸,但是因为一些原因某个页面需要单独的尺寸适配这个时候我们就需要自己手动去适配了。具体怎么操作呢?
·针对Activity(重新设置尺寸适配)
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)
*
* 返回设计图上的设计尺寸, 单位 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(放弃适配)
public class CancelAdaptActivity extends AppCompatActivity implements CancelAdapt {
}
·针对Fragment
首先我们需要添加到Fragment的支持
AutoSizeConfig.getInstance().setCustomFragment(true);
·针对Fragment(重新设置尺寸适配)
public class CustomAdaptFragment extends Fragment implements CustomAdapt {
@Override
public boolean isBaseOnWidth() {
return false;
}
@Override
public float getSizeInDp() {
return 667;
}}
·针对Fragment(放弃适配)
public class CancelAdaptFragment extends Fragment implements CancelAdapt {
}
关注点五:适配第三方库和控件
在使用主单位时可以使用ExternalAdaptManager来实现在不修改第三方库源码的情况下完成适配,具体实现为:
(1)添加适配
AutoSizeConfig.*getInstance*().getExternalAdaptManager().addExternalAdaptInfoOfActivity(TargetActivity.class, new ExternalAdaptInfo(true, 400));
其中TargetActivity.class就是我们需要适配的目标Activity,true代表以宽度作为基准,400代表的是基准宽度为400dp。
(2)取消适配
AutoSizeConfig.*getInstance*().getExternalAdaptManager().addCancelAdaptOfActivity(TargetActivity.class);
其中TargetActivity.class就是我们需要取消适配的目标Activity。
需要注意的是,ExternalAdaptManager的方法虽然可以添加任何类,但是只能支持 Activity、Fragment,并且 ExternalAdaptManager 是支持链式调用的,以便于持续添加多个页面。当然,ExternalAdaptManager 也可以使用在自己项目上的Activity和Fragment上。