概述:平時開發(fā),我們只需要在Activity的onCreate()方法中調(diào)用setContentView()方法就能實現(xiàn)頁面的展示,同時也能調(diào)用findViewById()獲取到對應(yīng)的控件實例,那么layout的XML文件到底是怎么轉(zhuǎn)化成View呢?
1、探索入口:setContentView()
public class MainActivity extends Activity{
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
}
}
-->調(diào)用Activity的setContentView
public class Activity{
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
...
}
}
-->調(diào)用了Window類的setContentView
public class Window{
public abstract void setContentView(@LayoutRes int layoutResID);
}
-->Window類的唯一實現(xiàn)類是PhoneWindow,最終是調(diào)用到了PhoneWindow的setContentView方法
public class PhoneWindow{
private LayoutInflater mLayoutInflater;
public PhoneWindow(Context context) {
super(context);
mLayoutInflater = LayoutInflater.from(context);
}
@Override
public void setContentView(int layoutResID) {
...
mLayoutInflater.inflate(layoutResID, mContentParent);
...
}
}
分析:
- Activity的setContentView()方法最終是調(diào)用了PhoneWindow中的setContentView();
- PhoneWindow中的setContentView()是通過LayoutInflater的inflate(int resource, ViewGroup root)方法;
- layoutResID的xml構(gòu)建成View之后,會添加為mContentParent的子View(我們設(shè)置的布局并非頁面的根布局,需要了解mContentParent請看mWindow.getDecorView);
2、LayoutInflater 的初始化
@SystemService(Context.LAYOUT_INFLATER_SERVICE)
public abstract class LayoutInflater {
public static LayoutInflater from(Context context) {
LayoutInflater LayoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}
}
-->通過調(diào)用ContextImpl類的getSystemService()方法
class ContextImpl extends Context {
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
}
-->通過系統(tǒng)服務(wù)管理類來獲取系統(tǒng)服務(wù)
/**
* Manages all of the system services that can be returned by {@link Context#getSystemService}.
* Used by {@link ContextImpl}.
*/
final class SystemServiceRegistry {
private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
new HashMap<String, ServiceFetcher<?>>();
--> 所有的系統(tǒng)服務(wù)都是在靜態(tài)代碼塊中注冊的,
static {
...
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher<LayoutInflater>() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}});
...
}
//把系統(tǒng)服務(wù)的名稱和系統(tǒng)服務(wù)保存到常量Hashmap中
private static <T> void registerService(String serviceName, Class<T> serviceClass,
ServiceFetcher<T> serviceFetcher) {
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}
//通過服務(wù)名稱獲取對應(yīng)的系統(tǒng)服務(wù)
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
}
分析:
- LayoutInflater實例的獲取只能通過其內(nèi)部的靜態(tài)方法from()獲取;
- context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)實際上是通過SystemServiceRegistry的getSystemService()方法獲取,調(diào)用一個常量的HashMap獲取對應(yīng)的服務(wù);
- 所有的系統(tǒng)服務(wù)都是在SystemServiceRegistry的靜態(tài)代碼塊中注冊保存的,這保證了服務(wù)不會被重復(fù)注冊保存;
- 所以LayoutInflater的實例獲取其實是一個單例設(shè)計模式。
3、mLayoutInflater.inflate(layoutResID, mContentParent)
public abstract class LayoutInflater {
/**
* Inflate a new view hierarchy from the specified xml resource. Throws
* {@link InflateException} if there is an error.
*/
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();//獲取Resources資源類
...
final XmlResourceParser parser = res.getLayout(resource);//獲取布局文件對應(yīng)xml解析器
try {
return inflate(parser, root, attachToRoot);//繼續(xù)調(diào)用下一個方法
} finally {
parser.close();
}
}
}
這里只是獲取Resources資源類,然后獲取xml文件對應(yīng)的xml解析器,再調(diào)用下一個inflate方法 。
3.1、開始解析布局xml文件
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
...
final AttributeSet attrs = Xml.asAttributeSet(parser);//獲取xml的屬性集合
...
View result = root;
try {
//判斷是否有開始結(jié)束標(biāo)簽
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
// Empty
}
if (type != XmlPullParser.START_TAG) {
throw new InflateException(parser.getPositionDescription()
+ ": No start tag found!");
}
final String name = parser.getName();//獲取標(biāo)簽名
...
if (TAG_MERGE.equals(name)) {
if (root == null || !attachToRoot) {
//如果父布局為空或者不需要添加到父布局之中,拋出異常,不直接解析merge為根標(biāo)簽的布局
throw new InflateException("<merge /> can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
//填充merge的xml布局,往下查看3.2
rInflate(parser, root, inflaterContext, attrs, false);
} else {
//轉(zhuǎn)換xml的根標(biāo)簽為temp,查看3.3
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
//配置根布局temp的布局參數(shù)
ViewGroup.LayoutParams params = null;
if (root != null) {
// Create layout params that match root, if supplied
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
temp.setLayoutParams(params);
}
}
//填充跟標(biāo)簽的子view,往下查看3.2
rInflateChildren(parser, temp, attrs, true);
//判斷一下,把我們的布局根元素對應(yīng)的view,添加為root的子view
if (root != null && attachToRoot) {
root.addView(temp, params);
}
//把我們的布局根元素對應(yīng)的view作為結(jié)果返回回去
if (root == null || !attachToRoot) {
result = temp;
}
}
}
...
finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
}
return result;
}
}
分析:
- 先從XmlPullParser獲取到所有的屬性集合,標(biāo)簽轉(zhuǎn)化為view時,需要用到;
- 判斷是否有開始結(jié)束標(biāo)簽,然后獲取第一個標(biāo)簽,判斷第一個標(biāo)簽是否為merge標(biāo)簽;
- 如果是merge標(biāo)簽,root不為空且要添加為root的子view時,調(diào)用rInflate方法解析其余標(biāo)簽;
- 如果第一個標(biāo)簽不是merge標(biāo)簽,調(diào)用createViewFromTag方法把此標(biāo)簽轉(zhuǎn)化成view,然后調(diào)用rInflateChildren方法解析其余標(biāo)簽;然后判斷是否需要把第一個標(biāo)簽添加為root的子view。
3.2、遞歸調(diào)用,遍歷xml的所有標(biāo)簽
-->填充parent的所有子view,
final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException {
rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
}
-->對parser進行循環(huán)獲取,解析所有的標(biāo)簽
void rInflate(XmlPullParser parser, View parent, Context context,
AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
final int depth = parser.getDepth();//獲取標(biāo)簽嵌套的深度,即布局嵌套的深度
int type;
boolean pendingRequestFocus = false;
//循環(huán)解析所有標(biāo)簽
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
final String name = parser.getName();
if (TAG_REQUEST_FOCUS.equals(name)) {//<requestFocus />標(biāo)簽
pendingRequestFocus = true;
consumeChildElements(parser);//
} else if (TAG_TAG.equals(name)) {//<tag />標(biāo)簽
parseViewTag(parser, parent, attrs);//給parent設(shè)置tag
} else if (TAG_INCLUDE.equals(name)) {//<include />標(biāo)簽
if (parser.getDepth() == 0) {
throw new InflateException("<include /> cannot be the root element");
}
parseInclude(parser, context, parent, attrs);
} else if (TAG_MERGE.equals(name)) {
throw new InflateException("<merge /> must be the root element");
} else {
final View view = createViewFromTag(parent, name, context, attrs);//把標(biāo)簽轉(zhuǎn)換成View,往下看3.3
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflateChildren(parser, view, attrs, true);//對view包裹的子view進行填充
viewGroup.addView(view, params);//添加為parent的子view
}
}
...
if (finishInflate) {
parent.onFinishInflate();//調(diào)用父View的onFinishInflate方法
}
}
/**
* 循環(huán)parser當(dāng)前深度下,所有嵌套的標(biāo)簽,但不進行任何操作;
* 即跳過某個標(biāo)簽和這個標(biāo)簽包裹的所有子標(biāo)簽
*/
final static void consumeChildElements(XmlPullParser parser)
throws XmlPullParserException, IOException {
int type;
final int currentDepth = parser.getDepth();
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
// Empty
}
}
分析:
- rInflateChildren和rInflate是個遞歸方法,會互相調(diào)用;
- rInflate方法中循環(huán)所有所有的標(biāo)簽,對標(biāo)簽進行判斷;
- 如果為<requestFocus />標(biāo)簽,那么跳過這個標(biāo)簽以及requestFocus嵌套下的所有子標(biāo)簽;
- 如果是<tag />標(biāo)簽,那么獲取ViewTag主題,給parent設(shè)置tag,然后跳過;
- 如果為merge標(biāo)簽, 那么merge包裹的標(biāo)簽,會填充merge的子標(biāo)簽,最后也會重新調(diào)用rInflate的方法;
- 然后對普通的View標(biāo)簽調(diào)用createViewFromTag方法,轉(zhuǎn)換成view;把這個view添加到父view中,然后調(diào)用rInflateChildren,遞歸填充此標(biāo)簽嵌套的子標(biāo)簽(如果存在子標(biāo)簽)。
3.3、把xml中的標(biāo)簽轉(zhuǎn)換為View
public abstract class LayoutInflater {
.
private Factory mFactory;
private Factory2 mFactory2;
private Factory2 mPrivateFactory;
//我們可以實現(xiàn)LayoutInflater的Factory接口,再調(diào)用setFactory(),可以hook我們的布局View創(chuàng)建
public interface Factory {
public View onCreateView(String name, Context context, AttributeSet attrs);
}
-->中轉(zhuǎn)方法,往下調(diào)用
private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) {
return createViewFromTag(parent, name, context, attrs, false);
}
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
...
// 給context加上主題屬性
if (!ignoreThemeAttr) {
final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
final int themeResId = ta.getResourceId(0, 0);
if (themeResId != 0) {
context = new ContextThemeWrapper(context, themeResId);
}
ta.recycle();
}
...
try {
View view;
//如果是繼承Activity,而且沒有實現(xiàn)Factory接口,mFactory和mFactory2都為空
//如果是繼承了AppCompatActivity,mFactory2不為空,onCreateView會在AppCompatDelegateImplV9中被調(diào)用
if (mFactory2 != null) {
view = mFactory2.onCreateView(parent, name, context, attrs);
} else if (mFactory != null) {
view = mFactory.onCreateView(name, context, attrs);
} else {
view = null;
}
//Activity賦值了mPrivateFactory,但是Activity類中的onCreateView實現(xiàn)為空,所以View依然為空
if (view == null && mPrivateFactory != null) {
view = mPrivateFactory.onCreateView(parent, name, context, attrs);
}
if (view == null) {
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
//判斷是否為自定義的View,有.的是自定義view,沒有.的是系統(tǒng)的View
if (-1 == name.indexOf('.')) {
view = onCreateView(parent, name, attrs);//往下看3.4
} else {
view = createView(name, null, attrs);
}
} finally {
mConstructorArgs[0] = lastContext;
}
}
return view;
}
...
}
}
分析:
- 首先把context轉(zhuǎn)換成ContextThemeWrapper,可以帶上主題屬性,傳遞給View的創(chuàng)建;
- 調(diào)用view的創(chuàng)建之前,會先判斷mFactory,mFactory2和mPrivateFactory是否為空;
- 通常如果我們繼承的是Activity類,mFactory和mFactory2是null,沒有被賦值,而mPrivateFactory雖然在Activity中設(shè)置了,但是Factory得onCreateView方法沒有被復(fù)寫,所以最后View的創(chuàng)建會調(diào)用onCreateView()方法(3.4);
- 如果我們繼承的是AppCompatActivity,那么會調(diào)用mFactory2接口的onCreateView方法,往下看4;
3.4、利用反射創(chuàng)建name對應(yīng)的View
public abstract class LayoutInflater {
//用于傳遞給view的構(gòu)造方法,保存和context和attr
final Object[] mConstructorArgs = new Object[2];
private Filter mFilter;//View實例化過濾器
private HashMap<String, Boolean> mFilterMap;//過濾器記錄
//讓我們定義是否允許這個Class對象的創(chuàng)建
public interface Filter {
boolean onLoadClass(Class clazz);
}
//用于給我們設(shè)置過濾器,可以過濾某些view的創(chuàng)建
public void setFilter(Filter filter) {
mFilter = filter;
if (filter != null) {
mFilterMap = new HashMap<String, Boolean>();
}
}
//用來保存所有View的構(gòu)造方法
private static final HashMap<String, Constructor<? extends View>> sConstructorMap =
new HashMap<String, Constructor<? extends View>>();
-->中轉(zhuǎn)方法,往下調(diào)用
protected View onCreateView(View parent, String name, AttributeSet attrs)
throws ClassNotFoundException {
return onCreateView(name, attrs);
}
-->中轉(zhuǎn)方法,拼接系統(tǒng)View的全類名,再往下調(diào)用
protected View onCreateView(String name, AttributeSet attrs)
throws ClassNotFoundException {
return createView(name, "android.view.", attrs);//系統(tǒng)的View要拼接全類名
}
-->傳入name,通過反射實例化對應(yīng)的View
public final View createView(String name, String prefix, AttributeSet attrs)
throws ClassNotFoundException, InflateException {
Constructor<? extends View> constructor = sConstructorMap.get(name);//獲取構(gòu)造方法
//判斷constructor的類加載器和LayoutInflater或者Context的類加載器是否為同一個
if (constructor != null && !verifyClassLoader(constructor)) {
constructor = null;
sConstructorMap.remove(name);
}
Class<? extends View> clazz = null;
try {
if (constructor == null) {
//獲取對應(yīng)View的Class對象
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View.class);
if (mFilter != null && clazz != null) {//Filter接口不設(shè)置的話,mFilter為空
boolean allowed = mFilter.onLoadClass(clazz);
if (!allowed) {
failNotAllowed(name, prefix, attrs);
}
}
constructor = clazz.getConstructor(mConstructorSignature);//獲取View的構(gòu)造方法
constructor.setAccessible(true);
sConstructorMap.put(name, constructor);//緩存構(gòu)造方法
} else {
// If we have a filter, apply it to cached constructor
if (mFilter != null) {
// Have we seen this name before?
Boolean allowedState = mFilterMap.get(name);//獲取過濾記錄
if (allowedState == null) {
// 獲取Class對象
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View.class);
//如果Class對象不為空,且onLoadClass返回true,過濾這個view的創(chuàng)建
boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
mFilterMap.put(name, allowed);//記錄是否過濾這個view的實例化
if (!allowed) {
failNotAllowed(name, prefix, attrs);//拋出InflateException異常
}
} else if (allowedState.equals(Boolean.FALSE)) {
failNotAllowed(name, prefix, attrs);
}
}
}
//配置view構(gòu)造方法的參數(shù),
Object lastContext = mConstructorArgs[0];
if (mConstructorArgs[0] == null) {
// Fill in the context if not already within inflation.
mConstructorArgs[0] = mContext;
}
Object[] args = mConstructorArgs;
args[1] = attrs;
final View view = constructor.newInstance(args);//調(diào)用反射,實例化view
if (view instanceof ViewStub) {
// Use the same context when inflating ViewStub later.
final ViewStub viewStub = (ViewStub) view;
viewStub.setLayoutInflater(cloneInContext((Context) args[0]));//設(shè)置viewStub的LayoutInflater,用于后期填充viewStub對應(yīng)的view
}
mConstructorArgs[0] = lastContext;
return view;
}
...
}
}
分析:
- 實例化View是在createView方法里面;
- 首先從構(gòu)造方法的緩存里面獲取View的構(gòu)造方法,判斷一下類加載器和LayoutInflater.class或者context的類加載器是否相同;
- 如果View的構(gòu)造方法為空,判斷一下是否設(shè)置了這個View的實例化過濾,反射獲取這個View的構(gòu)造方法,并記錄起來;
- 如果View的構(gòu)造方法不為空,判斷是否設(shè)置了View過濾器,記錄是否過濾這個view的實例化;
- 最后調(diào)用constructor.newInstance()方法實例化View;
4、當(dāng)我們繼承AppCompatActivity,布局的填充會是另一種方式
-->如果繼承的是AppCompatActivity,那么setContentView會調(diào)用到AppCompatActivity里面來
public class AppCompatActivity extends FragmentActivity implements AppCompatCallback,
TaskStackBuilder.SupportParentable, ActionBarDrawerToggle.DelegateProvider {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
final AppCompatDelegate delegate = getDelegate();
//delegate是AppCompatDelegateImplV9的子類,最終會調(diào)用到AppCompatDelegateImplV9里面的installViewFactory方法
delegate.installViewFactory(); //這里會設(shè)置LayoutInflater的mFractory2,下面會講到
...
}
-->調(diào)用到這里的setContentView
@Override
public void setContentView(@LayoutRes int layoutResID) {
//先獲取AppCompatDelegate
getDelegate().setContentView(layoutResID);
}
-->往下走getDelegate()
@NonNull
public AppCompatDelegate getDelegate() {
if (mDelegate == null) {
mDelegate = AppCompatDelegate.create(this, this);
}
return mDelegate;
}
}
-->調(diào)用到AppCompatDelegate里面的create
public abstract class AppCompatDelegate {
public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
return create(activity, activity.getWindow(), callback);
}
//下面的幾個類都都是AppCompatDelegateImplV9的子類
private static AppCompatDelegate create(Context context, Window window,
AppCompatCallback callback) {
if (Build.VERSION.SDK_INT >= 24) {
return new AppCompatDelegateImplN(context, window, callback);
} else if (Build.VERSION.SDK_INT >= 23) {
return new AppCompatDelegateImplV23(context, window, callback);
} else if (Build.VERSION.SDK_INT >= 14) {
return new AppCompatDelegateImplV14(context, window, callback);
} else if (Build.VERSION.SDK_INT >= 11) {
return new AppCompatDelegateImplV11(context, window, callback);
} else {
return new AppCompatDelegateImplV9(context, window, callback);//最后會調(diào)用AppCompatDelegateImplV9的setConetentView()
}
}
}
當(dāng)我們設(shè)置了繼承AppCompatActivity,內(nèi)部會通過getDelegate()方法,最終能獲取到AppCompatDelegateImplV9獲取其子類;
@RequiresApi(14)
class AppCompatDelegateImplV9 extends AppCompatDelegateImplBase
implements MenuBuilder.Callback, LayoutInflater.Factory2 {
-->這個方法在AppCompatActivity的onCreate()中被調(diào)用,設(shè)置LayoutInflater的mFactory2
@Override
public void installViewFactory() {
LayoutInflater layoutInflater = LayoutInflater.from(mContext);
if (layoutInflater.getFactory() == null) {
LayoutInflaterCompat.setFactory2(layoutInflater, this);
} else {
if (!(layoutInflater.getFactory2() instanceof AppCompatDelegateImplV9)) {
Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed"
+ " so we can not install AppCompat's");
}
}
}
-->LayoutInflater.Factory2的接口方法,LayoutInflater中的mFactory2.onCreateView會回調(diào)進來這個方法
@Override
public final View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
//這里會回調(diào)我們的Activity中的onCreateView方法,如果沒有實現(xiàn),那么返回null
final View view = callActivityOnCreateView(parent, name, context, attrs);
if (view != null) {
return view;
}
//往下調(diào)用createView方法實例化View
return createView(parent, name, context, attrs);
}
//回調(diào)Activity的onCreateView,如果我們有復(fù)寫,返回onCreateView方法的結(jié)果
View callActivityOnCreateView(View parent, String name, Context context, AttributeSet attrs) {
// Let the Activity's LayoutInflater.Factory try and handle it
if (mOriginalWindowCallback instanceof LayoutInflater.Factory) {
final View result = ((LayoutInflater.Factory) mOriginalWindowCallback)
.onCreateView(name, context, attrs);
if (result != null) {
return result;
}
}
return null;
}
//中轉(zhuǎn)方法,用來加入一些判斷
@Override
public View createView(View parent, final String name, @NonNull Context context,
@NonNull AttributeSet attrs) {
if (mAppCompatViewInflater == null) {
mAppCompatViewInflater = new AppCompatViewInflater();
}
boolean inheritContext = false;
if (IS_PRE_LOLLIPOP) {//如果sdk版本低于21,就是5.0以下的手機
inheritContext = (attrs instanceof XmlPullParser)
// If we have a XmlPullParser, we can detect where we are in the layout
? ((XmlPullParser) attrs).getDepth() > 1
// Otherwise we have to use the old heuristic
: shouldInheritContext((ViewParent) parent);
}
//通過AppCompatViewInflater的createView方法實例化View
return mAppCompatViewInflater.createView(parent, name, context, attrs, inheritContext,
IS_PRE_LOLLIPOP, /* Only read android:theme pre-L (L+ handles this anyway) */
true, /* Read read app:theme as a fallback at all times for legacy reasons */
VectorEnabledTintResources.shouldBeUsed() /* Only tint wrap the context if enabled */
);
}
}
分析:
- installViewFactory()方法在AppCompatActivity的onCreate()方法中被調(diào)用,也就是我們自己的Activity的onCreate()方法;
- installViewFactory()方法中調(diào)用了LayoutInflaterCompat.setFactory2(layoutInflater, this),這里給LayoutInflater設(shè)置了mFractory2;
- 也就是我們在調(diào)用setContentView()方法之前,就先設(shè)置好了LayoutInflater的mFractory2,那么LayoutInflater的createViewFromTag()方法會回調(diào)進來AppCompatDelegateImplV9的onCreateView(LayoutInflater.Fractory2接口方法)方法;
- 經(jīng)過一連串的調(diào)用,如果我們沒有實現(xiàn)onCreateView方法,那么最終會調(diào)用AppCompatViewInflater的createView,來實例化View;
class AppCompatViewInflater {
public final View createView(View parent, final String name, @NonNull Context context,
@NonNull AttributeSet attrs, boolean inheritContext,
boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
final Context originalContext = context;
//配置一下主題,上下文,略過
...
View view = null;
//根據(jù)標(biāo)簽名,返回android.support.v7.widget中的控件
//如果是繼承Activity,返回的是android.view中的控件
switch (name) {
case "TextView":
view = new AppCompatTextView(context, attrs);
break;
case "ImageView":
view = new AppCompatImageView(context, attrs);
break;
case "Button":
view = new AppCompatButton(context, attrs);
break;
case "EditText":
view = new AppCompatEditText(context, attrs);
break;
case "Spinner":
view = new AppCompatSpinner(context, attrs);
break;
case "ImageButton":
view = new AppCompatImageButton(context, attrs);
break;
case "CheckBox":
view = new AppCompatCheckBox(context, attrs);
break;
case "RadioButton":
view = new AppCompatRadioButton(context, attrs);
break;
case "CheckedTextView":
view = new AppCompatCheckedTextView(context, attrs);
break;
case "AutoCompleteTextView":
view = new AppCompatAutoCompleteTextView(context, attrs);
break;
case "MultiAutoCompleteTextView":
view = new AppCompatMultiAutoCompleteTextView(context, attrs);
break;
case "RatingBar":
view = new AppCompatRatingBar(context, attrs);
break;
case "SeekBar":
view = new AppCompatSeekBar(context, attrs);
break;
}
//如果view還是空,那么就走反射實例化View
if (view == null && originalContext != context) {
view = createViewFromTag(context, name, attrs);
}
if (view != null) {
// If we have created a view, check its android:onClick
checkOnClickListener(view, attrs);
}
return view;
}
}
這里其實也很easy,判斷一下標(biāo)簽名,直接新建一個android.support.v7.widge包中的控件;如果還是為空,就調(diào)用createViewFromTag利用反射實例化;
總結(jié):
1、如果我們的MainActivity設(shè)置繼承Activity,那么setContentView是會調(diào)用PhoneWindow的setContentView(),然后調(diào)用LayoutInflater.inflater()方法進行布局填充;
2、LayoutInflater中首先會用XmlPullParser對布局文件進行解析,然后循環(huán)遍歷xml中的所有標(biāo)簽;
3、對標(biāo)簽的名字進行判斷,處理一些特殊標(biāo)簽,然后普通的View標(biāo)簽調(diào)用createViewFromTag方法進行轉(zhuǎn)換;
4、createViewFromTag方法會判斷一下是否有設(shè)置Factory接口并且實現(xiàn)onCreateView方法來實例化View,最后如果view還是為空,則調(diào)用createView方法,通過標(biāo)簽的全類名反射進行View的實例化;
5、如果我們的MainActivity設(shè)置繼承AppCompatActivity,AppCompatActivity的onCreate中會設(shè)置LayoutInflater的mFactory2,對View的實例化進行代理;
6、AppCompatDelegateImplV9類中繼承了LayoutInflater.Factory2方法,并實現(xiàn)了onCreateView方法,那么View的實例化由AppCompatDelegateImplV9這里進行主導(dǎo);
7、AppCompatDelegateImplV9會調(diào)用AppCompatViewInflater的createView方法實例化View,對標(biāo)簽名進行判斷,如果是系統(tǒng)的控件名,返回android.support.v7.widge包中對應(yīng)的控件,其他控件則用反射進行實例化。