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