简述
在很久很久以前,android的activity时继承Activity的,但在后来却改成了AppCompatActivity,那AppCompatActivity是什么呢?继承他有什么用呢?
其实他最主要的工作是5.0之后的View兼容和页面主题相关处理,例如AppCompatTextView之类的内部都是有兼容操作的,而继承AppCompatActivity后,其会在内部将TextView替换为AppCompatTextView(之前一直在想使用TextView好还是AppCompatTextView好,其实都一样,其内部会进行替换),那这里就来分析下它是怎么进行替换的.
分析
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的创建,在Activity创建View时会调用其Factory进行创建,其调用过程是
Activyty
|
setContentView()
|
PhoneView.setContentView()
|
LayoutInflater.inflate()
|
createViewFromTag()
createViewFromTag的实现为
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
...
try {
View view;
if (mFactory2 != null) {//检查是否设置的Factory2,有就调用其来创建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 是内部的工厂,activity中的实现为空实现
view = mPrivateFactory.onCreateView(parent, name, context, attrs);
}
if (view == null) {//此处的View未建立则会通过反射来进行创建
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的创建进行拦截,并自定义其创建方式
AppCompatActivity的onCreate
AppCompatActivity的替换操作主要在onCreate进行,这里主要做的是设置Factory2(其继承了Factory,所以内部需要实现两个创建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);//设置View创建工厂
} 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来设置,而且设置前会检查时候已经设置,如果已经设置了就会报错.
其Factory的实现为
/**
* 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);//此处是调用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);//此处进行兼容性View的替换
}
/**
* From {@link LayoutInflater.Factory2}.
*/
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
return onCreateView(null, name, context, attrs);//直接调用上面的方法
}
在createView的一系列的操作后会进行兼容性View的替换,具体实现在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) {//创建兼容性View
case "TextView":
view = createTextView(context, attrs);//返回AppCompatTextView,以下类似
verifyNotNull(view, name);//空监测,为空会直接抛异常
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);//这里是空实现
}
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;
}
由此可见,在创建View 时AppCompatActivity将TextView创建为AppCompatTextView
End
综上所诉,AppCompatActivity通过Factory将View替换为具有兼容性的View,当然AppCompatActivity其中也很多其他的操作,这里没有详细去说明,若文中有什么错误的请指出纠正,谢谢哟~~~