1.compat库是如何将TextView
替换为AppCompatTextView
的? 2.为何要进行替换? 3.根据替换相关原理,咱们能够作哪些事情?android
Answer1:bash
先从第二问开始吧,AppCompatTextView
继承自TextView
,是对TextView
的一种扩展,由于在5.0中首次推出了MaterialDesign
这种设计风格,可是众所周知,5.0推出不可能全部的设备所有都更新到最新版本,为了在早期版本上实现新的功能(这些新功能好比从源码注释中解读到backgroundTint
属性,根据文本内容自适应大小等),即为了新特性一样能够兼容老版本,framework在建立TextView
实例的时候,自动帮咱们进行了替换。其它的AppCompatXXX与XXX的关系也是如此。布局
而后第一问,是如何完成替换的,咱们这里只拿最直观的流程举例,且尽量的简化源码过程,在讨论这个问题以前,先了解几个预备知识:ui
mLayoutInflater.inflate(layoutResID,mContentParent);
2.inflate方法,该方法的做用是将指定的XML文件填充到View的层次结构中去,最终不管经过什么途径调用到inflate方法,都会走到三个参数的重载方法这里:return inflate(parser,root,attachToRoot);
复制代码
parser你能够认为持有将Layout.XML解析后的数据,后两个参数的意义以下:spa
LayoutParams
设置给当前的XML布局。 知道了LayoutInflate.inflate作了什么,再往下,inflate中会调用到createViewFromTag
,从方法名就能知道,继续往下走,咱们离答案愈来愈近了。 createViewFromTag
作的事情很是有意思:View view;
if(mFactory2 !=null){
view =mFactory2.onCreateView(parent,name,context,attrs);
}else if(mFactory !=null){
view =mFactory.onCreateView(name,context,attrs);
}else{
view =null;
}
if(view == null && mPrivateFacotry !=null){
view =mPrivateFacotry.onCreateVoew(parent,name,context,attrs);
}
if(view ==null){
final Object lastContext =mConstructorArgs[0];
mConstructorArgs[0]=context;
try{
if(-1 == name.indexOf('.')){ //①
view =onCreateView(parent,name,attrs);
}else{
view =createVoew(name,null,attrs);
}
}finally{
mConstructorArgs[0]=lastContext;
}
}
return view;
复制代码
先看①这个if-else,条件是name中有没有字符“.”,若是有咱们会执行onCreateView
,若是没有会执行createView。name啥时候有点?自定义控件的时候。当是系统控件的时候,createView
会有一个填充了第二个参数的调用:createVoew(name,"android.view.",attrs);
补上了View控件的全路径名,而自定义控件则不须要,由于传入的name就是一个全路径名。 为何要全路径名?由于View控件对象的建立是经过反射来实现的:.net
clazz =mContext.getClassLoader().loadClass(
prefix!= null ? (prefix+name):name).asSubclass(View.class);
...
constructor=clazz.getConstructor(mConstructorSignature);
constructor.setAccessible(true);
sConstructorMap.put(name,constructor);
...
args[1]=attrs;
final View view =constructor.newInstance(args);
复制代码
下面对这几布作一个总结: XML中保存了ViewTree
的结构和View的相关标签信息(包括View的类型和一些属性值),而后这些信息会在后面经过反射的方式(若是没有Factory2和Factory的话)建立实例对象,若是建立的是ViewGroup
,则会对它的子View遍历重复建立步骤,建立完View对象后,会add到对应的ViewGroup
中,其中相关方法调用流程是:inflate->rInflate->createViewFromTag->createView
好像仍是没有看到资源替换? 咱们只解释了后半部分,没有解释前半部分,那么什么是Factory? 继续往下看: createViewFromTag中会先判断有没有Factory或者Factory2的对象,若是有,则调用Factory的onCreateView方法,这两个类都是接口,其中Factory2是Factory的子接口,都只有惟一一个onCreateView方法,不一样之处在于Factory2的onCreateView
方法传入了parentView
该方法的做用就是你能够借助它来改造XML中已经存在了的Tag的值。全部Factory2能够达到改造parentView的目的。 可是咱们平常中根本就没有任何地方接触到了Factory2,那么它是否是就直接是null呢?到这里又是一番源码调来调去,为了便于理解,只须要知道,这个Factory2,在最开始AppCompatActivity
(为了兼容低版本,咱们如今Activity默认都继承自它)中的onCreate
方法中就已经经过层层调用被设置好了。 既然如今Factory2不为空,那么就应该去走它的onCreateView方法了,这里又是层层调用,最终来到了AppCompatViewInflater的onCreateView方法: 设计
compat
库是如何将TextView替换为
AppCompatTextView
的?
我的对这个的理解:在将XML文件解析成包含ViewTree信息以后,开始利用这些信息去建立每个View节点,在建立View对象的时候,若是发现这个节点是属于支持兼容的控件好比
TextView
,那么就会去调用到new AppCompatTextView()
来建立一个兼容View对象,也就是在建立的时候,就已经实现了替换。code
第三问: 根据替换原理,咱们能够作哪些事情? 整个替换从上面的源码中就能够看到,可以被替换的关键是Factory2
存在,那么我以为,其实问题问的是Factory2能够用来作什么? 答案在这里cdn