簡述
在很久很久以前,android的activity時繼承Activity的,但在后來卻改成了AppCompatActivity,那AppCompatActivity是什么呢?繼承他有什么用呢?
其實他最主要的工作是5.0之后的View兼容和頁面主題相關(guān)處理,例如AppCompatTextView之類的內(nèi)部都是有兼容操作的,而繼承AppCompatActivity后,其會在內(nèi)部將TextView替換為AppCompatTextView(之前一直在想使用TextView好還是AppCompatTextView好,其實都一樣,其內(nèi)部會進(jìn)行替換),那這里就來分析下它是怎么進(jìn)行替換的.
分析
Factory
首先來說說Factory
public interface Factory {
public View onCreateView(String name, Context context, AttributeSet attrs);
}
public interface Factory2 extends Factory {
public View onCreateView(View parent, String name, Context context, AttributeSet attrs);
}
Factory的作用是對View的創(chuàng)建,在Activity創(chuàng)建View時會調(diào)用其Factory進(jìn)行創(chuàng)建,其調(diào)用過程是
Activyty
|
setContentView()
|
PhoneView.setContentView()
|
LayoutInflater.inflate()
|
createViewFromTag()
createViewFromTag的實現(xiàn)為
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
...
try {
View view;
if (mFactory2 != null) {//檢查是否設(shè)置的Factory2,有就調(diào)用其來創(chuàng)建View
view = mFactory2.onCreateView(parent, name, context, attrs);
} else if (mFactory != null) {
view = mFactory.onCreateView(name, context, attrs);
} else {
view = null;
}
if (view == null && mPrivateFactory != null) {//mPrivateFactory 是內(nèi)部的工廠,activity中的實現(xiàn)為空實現(xiàn)
view = mPrivateFactory.onCreateView(parent, name, context, attrs);
}
if (view == null) {//此處的View未建立則會通過反射來進(jìn)行創(chuàng)建
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
if (-1 == name.indexOf('.')) {
view = onCreateView(parent, name, attrs);
} else {
view = createView(name, null, attrs);
}
} finally {
mConstructorArgs[0] = lastContext;
}
}
return view;
} ...//一系列異常處理
}
由此可見,使用Factory可以對所有的View的創(chuàng)建進(jìn)行攔截,并自定義其創(chuàng)建方式
AppCompatActivity的onCreate
AppCompatActivity的替換操作主要在onCreate進(jìn)行,這里主要做的是設(shè)置Factory2(其繼承了Factory,所以內(nèi)部需要實現(xiàn)兩個創(chuàng)建View的方法)
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
...
delegate.installViewFactory();//裝載Factory2
...
super.onCreate(savedInstanceState);
}
而具體的裝載過程在AppCompatDelegateImplV9中
@Override
public void installViewFactory() {
LayoutInflater layoutInflater = LayoutInflater.from(mContext);
if (layoutInflater.getFactory() == null) {
LayoutInflaterCompat.setFactory2(layoutInflater, this);//設(shè)置View創(chuàng)建工廠
} 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");
}
}
}
可以看出AppCompatActivity通過setFactory2來設(shè)置,而且設(shè)置前會檢查時候已經(jīng)設(shè)置,如果已經(jīng)設(shè)置了就會報錯.
其Factory的實現(xiàn)為
/**
* From {@link LayoutInflater.Factory2}.
*/
@Override
public final View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
// First let the Activity's Factory try and inflate the view
final View view = callActivityOnCreateView(parent, name, context, attrs);//此處是調(diào)用Activity中的onCreateView方法
if (view != null) {
return view;
}
// If the Factory didn't handle it, let our createView() method try
return createView(parent, name, context, attrs);//此處進(jìn)行兼容性View的替換
}
/**
* From {@link LayoutInflater.Factory2}.
*/
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
return onCreateView(null, name, context, attrs);//直接調(diào)用上面的方法
}
在createView的一系列的操作后會進(jìn)行兼容性View的替換,具體實現(xiàn)在AppCompatViewInflater中
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;
// We can emulate Lollipop's android:theme attribute propagating down the view hierarchy
// by using the parent's context
if (inheritContext && parent != null) {
context = parent.getContext();
}
if (readAndroidTheme || readAppTheme) {
// We then apply the theme on the context, if specified
context = themifyContext(context, attrs, readAndroidTheme, readAppTheme);
}
if (wrapContext) {
context = TintContextWrapper.wrap(context);
}
View view = null;
// We need to 'inject' our tint aware Views in place of the standard framework versions
switch (name) {//創(chuàng)建兼容性View
case "TextView":
view = createTextView(context, attrs);//返回AppCompatTextView,以下類似
verifyNotNull(view, name);//空監(jiān)測,為空會直接拋異常
break;
case "ImageView":
view = createImageView(context, attrs);
verifyNotNull(view, name);
break;
case "Button":
view = createButton(context, attrs);
verifyNotNull(view, name);
break;
case "EditText":
view = createEditText(context, attrs);
verifyNotNull(view, name);
break;
case "Spinner":
view = createSpinner(context, attrs);
verifyNotNull(view, name);
break;
case "ImageButton":
view = createImageButton(context, attrs);
verifyNotNull(view, name);
break;
case "CheckBox":
view = createCheckBox(context, attrs);
verifyNotNull(view, name);
break;
case "RadioButton":
view = createRadioButton(context, attrs);
verifyNotNull(view, name);
break;
case "CheckedTextView":
view = createCheckedTextView(context, attrs);
verifyNotNull(view, name);
break;
case "AutoCompleteTextView":
view = createAutoCompleteTextView(context, attrs);
verifyNotNull(view, name);
break;
case "MultiAutoCompleteTextView":
view = createMultiAutoCompleteTextView(context, attrs);
verifyNotNull(view, name);
break;
case "RatingBar":
view = createRatingBar(context, attrs);
verifyNotNull(view, name);
break;
case "SeekBar":
view = createSeekBar(context, attrs);
verifyNotNull(view, name);
break;
default:
// The fallback that allows extending class to take over view inflation
// for other tags. Note that we don't check that the result is not-null.
// That allows the custom inflater path to fall back on the default one
// later in this method.
view = createView(context, name, attrs);//這里是空實現(xiàn)
}
if (view == null && originalContext != context) {
// If the original context does not equal our themed context, then we need to manually
// inflate it using the name so that android:theme takes effect.
view = createViewFromTag(context, name, attrs);
}
if (view != null) {
// If we have created a view, check its android:onClick
checkOnClickListener(view, attrs);
}
return view;
}
由此可見,在創(chuàng)建View 時AppCompatActivity將TextView創(chuàng)建為AppCompatTextView
End
綜上所訴,AppCompatActivity通過Factory將View替換為具有兼容性的View,當(dāng)然AppCompatActivity其中也很多其他的操作,這里沒有詳細(xì)去說明,若文中有什么錯誤的請指出糾正,謝謝喲~~~