下一節 : 安卓 - 源碼分析 - LayoutInflater(二)
先看一下LayoutInflater的說明:
/**
* LayoutInflater用於解析並根據xml佈局文件生成對應的View對象
* 該類不應該被直接調用,而是通過相關方法獲取:
* Activity.getLayoutInflater / Context.getSystemService
* 通過該方式,才能獲取正確綁定上下文的LayoutInflater對象。
*
* 如果你的View需要用額外的Factory去創建自定義的LayoutInflater,
* 可以cloneInContext複製一個LayoutInflater,之後,
* 通過setFactory方法將你的Factory添加到LayoutInflater中。
*
* 由於性能原因,View的填充過程重度依賴於xml文件的預處理,
* 所有目前LayoutInflater不支持在運行時直接解析未編譯的xml。
*/
類說明爲我們明確了3點:
不要直接創建該類,需要使用指定方法獲取LayoutInflater對象。
可以通過Factory對LayoutInflater的填充過程進行自定義;
可以對LayoutInflater複製並重新設置Factory。
LayoutInflater只能解析編譯過的xml佈局文件。
其中,所有指定的方法,最終都是通過Context.getSystemService實現:
/*
*內部實現:
* getWindow().getLayoutInflater()
* getWindow()得到的是Activity的mWindow對象:
* mWindow = new PhoneWindow(this, window);
* 即調用了PhoneWindow.getLayoutInflater(),其實現:
* mLayoutInflater = LayoutInflater.from(context);
*/
Activity.getLayoutInflater();
/*
* 內部實現:
* LayoutInflater factory = LayoutInflater.from(context);
* return factory.inflate(resource, root);
*/
View.inflate(Context context, @LayoutRes int resource, ViewGroup root);
/*
* 內部實現:
* LayoutInflater LayoutInflater = (LayoutInflater) context
* .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
*/
LayoutInflater.from(context);
然後,我們再看一下內部的幾個接口和對象:
private boolean mFactorySet; //是否調用過setFactory
private Factory mFactory; //繼承Factory的對象
private Factory2 mFactory2; //繼承Factory2的對象
private Factory2 mPrivateFactory; //由系統維護的對象
private Filter mFilter; //過濾器
/*
* 過濾器
* 如果要填充的View不被過濾器允許,將會拋出InflateException
*/
public interface Filter {
boolean onLoadClass(Class clazz);
}
/*
* Factory
* 3個參數分別爲:
* name :需要填充的Tag名稱,即View名稱;
* context :View所在的上下文環境;
* attrs :View在xml定義中定義的屬性,
* 當只處理個別View時,其他的View可以返回null。
*/
public interface Factory {
public View onCreateView(String name, Context context, AttributeSet attrs);
}
/*
* Factory2
* 重載onCreateView,增加了ViewParent參數。
*/
public interface Factory2 extends Factory {
public View onCreateView(View parent, String name,
Context context, AttributeSet attrs);
}
關於過濾器Filter:
通過setFilter方法,可以爲LayoutInflater設置自定義的過濾器
/*
* 爲LayoutInflater添加過濾器,當一個View不被過濾器允許時,
* 在填充過程的inflate方法裏將會拋出InflateException,
* 新設置的filter將會覆蓋之前所有設置的filter。
*/
public void setFilter(Filter filter) {
mFilter = filter;
if (filter != null)
mFilterMap = new HashMap<String, Boolean>();
}
注意新設置Filter的會覆蓋舊的Filter,同時mFilterMap重新初始化。
mFilterMap的作用:記錄Filter對不同View的過濾結果,當View進入過濾時,優先查找mFilterMap,避免Filter重複使用。
Filter的調用過程將會在後面的解析過程中分析。
關於Factory:
Factory可以讓你對需要填充的View進行自定義。
Factory接口中,方法參數有name、context、attrs,Factory2則多了一個增加parent參數的重載。
應該關注的參數是:name和attrs:
name:
需要填充的View的類名,即對應xml中的Tag名。例如TextView。
你可以根據這個類名自定義View的創建,可以創建對應的View,也可以創建另外的View。
如:AppCompatActivity處理TextView,會返回AppCompatTextView而不是TextView。
attrs:
View屬性,可以對這個屬性進行修改,例如添加新屬性,修改原有屬性等。
通過修改attrs,可以輕鬆打造換膚功能。
使用setFactory方法,對LayoutInflater添加自定義Factory:
/*
* 可以爲LayoutInflater添加一個實現Factory接口的類去改變View創建的過程,
* 這個Factory對象不能爲null,且只能設置一次,設置後將不能進行變更,
* 在解析xml佈局文件過程中,解析到View時,Factory會被調用,
* 如果Factory返回一個View,這個View則會被添加到控件層次中,
* 如果返回空,將會調用的默認的onCreateView方法創建View。
*/
public void setFactory(Factory factory) {
// 已設置與非空判定,判定不通過會拋異常
if (mFactorySet)
throw new IllegalStateException("A factory has already "
+ "been set on this LayoutInflater");
if (factory == null)
throw new NullPointerException("Given factory can not be null");
// mFactorySet默認爲false,這裏將mFactorySet設爲true
// 再次調用setFactory將拋出上面的異常
mFactorySet = true;
// 如果原有的LayoutInflater帶有mFactory
// 需要保留原有mFactory,下面再去介紹
if (mFactory == null)
mFactory = factory;
else
mFactory = new FactoryMerger(factory, null, mFactory, mFactory2);
}
也可以使用setFactory2,設置一個Factory2對象。
setFactory2和setFactory方法一樣,但同時會將Factory2設置爲mFactory和mFactory2:
public void setFactory2(Factory2 factory) {
...
if (mFactory == null) {
mFactory = mFactory2 = factory;
} else {
mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2);
}
}
上面涉及到了一個FactoryMerger類
private static class FactoryMerger implements Factory2 {
private final Factory mF1, mF2;
private final Factory2 mF12, mF22;
FactoryMerger(Factory f1, Factory2 f12, Factory f2, Factory2 f22) {
mF1 = f1; mF2 = f2; mF12 = f12; mF22 = f22;
}
public View onCreateView(String name, Context context, AttributeSet attrs) {
View v = mF1.onCreateView(name, context, attrs);
if (v != null) return v;
return mF2.onCreateView(name, context, attrs);
}
public View onCreateView(View parent, String name,
Context context, AttributeSet attrs) {
View v = mF12 != null ?
mF12.onCreateView(parent, name, context, attrs) :
mF1.onCreateView(name, context, attrs);
if (v != null) return v;
return mF22 != null ?
mF22.onCreateView(parent, name, context, attrs) :
mF2.onCreateView(name, context, attrs);
}
}
這個類實現了Factory2接口,即是Factory的實現類,
它的作用,其實就是保留上一個Factory作爲默認View創建方法。
例如:
現在有一個LayoutInflater:
LayoutInflater inflater;
inflater.setFactory((name, context, attrs) -> {
if(TextUtils.equals(name, TextView.class.getSimpleName()))
return new EditText(context,attrs);
return null;
})
這時候,想得到一個對EditView做處理,同時保留原來Factory行爲的新LayoutInflater:
// 複製原有的LayoutInflater
LayoutInflater newInflater = inflater.cloneInContext(context);
newInflater.setFactory((name, context, attrs) -> {
if(TextUtils.equals(name, EditText.class.getSimpleName()))
return new TextView(context,attrs);
return null;
})
由於newInflater已經存在一個Factory,所以setFactory會爲我們創建一個FactoryMerger對象:
public void setFactory(Factory factory) {
if (mFactory == null)
mFactory = factory;
else
mFactory = new FactoryMerger(factory, null, mFactory, mFactory2);
}
當填充EditText時,新的Factory會返回一個TextView對象;
而填充TextView時,則會返回null,這時會調用原有的Factory進行解析,即會返回一個EditText對象。
餘下的分析放在
下一節 : 安卓 - 源碼分析 - LayoutInflater(二)