以下以Android api28 为例
1. Activity ContentView 分析
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
那我们就要来根据源码来分析setContentView 做了什么事
1.1 Activity -> setContentView
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
public Window getWindow() {
return mWindow;
}
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback) {
....
mWindow = new PhoneWindow(this, window, activityConfigCallback);
....
}
1.2 从源码看 mWindow 对应的实现类 PhoneWindow
PhoneWindow 里面的setContentView(R.layout.activity_main)
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
//加载 contentView 传入的activity_main 至 mContentParent 视图上
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
生成decorView 视图 ,获取id为ID_ANDROID_CONTENT的FrameLayout的ViewGroup视图
ID_ANDROID_CONTENT 为R.id.content;
private void installDecor(){
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1); // 创建DecorView
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
}
}
protected DecorView generateDecor(int featureId) {
...
//返回 new 了一个 DecorView
return new DecorView(context, featureId, this, getAttributes());
}
protected ViewGroup generateLayout(DecorView decor) {
int layoutResource;
//一系列的判断,去加载系统布局
mDecor.startChanging();
//去加载系统的 layoutResource
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
....
mDecor.setWindowBackground(background);
mDecor.finishChanging();
return contentParent;
}
//添加系统布局到 DecorView
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {\
mDecorCaptionView = createDecorCaptionView(inflater);
final View root = inflater.inflate(layoutResource, null);
if (mDecorCaptionView != null) {
if (mDecorCaptionView.getParent() == null) {
addView(mDecorCaptionView,
new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mDecorCaptionView.addView(root,
new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
} else {
// Put it below the color views.
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mContentRoot = (ViewGroup) root;
initializeElevation();
}
1.3 视图的整体布局
PhoneWindow
->DectorView
->系统布局
R.id.content fragment
->自己调用setContentView 的布局
2. AppCompateActivity setContentView 分析
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
2.1 AppCompatActivity setContentView的源码
public void setContentView(@LayoutRes int layoutResID) {
this.getDelegate().setContentView(layoutResID);
}
public AppCompatDelegate getDelegate() {
if (this.mDelegate == null) {
this.mDelegate = AppCompatDelegate.create(this, this);
}
return this.mDelegate;
}
public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
return new AppCompatDelegateImpl(activity, activity.getWindow(), callback);
}
2.2 AppCompatDelegateImpl setContentView的源码
public void setContentView(int resId) {
this.ensureSubDecor();
ViewGroup contentParent = (ViewGroup)this.mSubDecor.findViewById(16908290);
contentParent.removeAllViews();
LayoutInflater.from(this.mContext).inflate(resId, contentParent);
this.mOriginalWindowCallback.onContentChanged();
}
通过debug 调试,我们发现 extends Activity 跟 AppCompatActivity 的区别
//extends Activity mImageIV是 ImageView
//而 extends AppCompatActivity mImageIV 是 AppCompatImageView
Log.e("TAG",""+mImageIv) ;
问题引出
为什么extends AppCompatActivity 的ImageView 会被替换成 AppCompatImageView 呢?
AppCompatActivity 会对View进行拦截,替换成自己的View
原因出在LayoutInflater 来inflater 我们的布局
我们现在来看看 LayoutInflater 的源码
3. LayoutInflater inflater 的源码
3.1 LayoutInflater.from(this) 获取的源码
public static LayoutInflater from(Context context) {
LayoutInflater LayoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
return LayoutInflater;
}
@Override
public Object getSystemService(String name){
return SystemSericeRegistry.getSystemService(this,name);
}
ServiceFetcher<?> fetcher = SYSTEM_SERVICES_FETCHERS.get(name)://从静态的map集合获取
LayoutInflater.from(this) 它是系统服务,是单例设计模式。
3.1 实例化View的inflater方法的源码
//方法2
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
//方法3
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
if (DEBUG) {
Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
+ Integer.toHexString(resource) + ")");
}
final XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
...
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
...
return result;
}
}
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
...
try {
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;
}
....
}
3.2 从这里可以看出是mFactory2 起了作用,再来看看什么时候设置了mFactory2
现在回到 AppCompatActivity oncreate方法 可以看到
delegate.installViewFactory(); 设置Factory2
protected void onCreate(@Nullable Bundle savedInstanceState) {
AppCompatDelegate delegate = this.getDelegate();
delegate.installViewFactory();
delegate.onCreate(savedInstanceState);
if (delegate.applyDayNight() && this.mThemeId != 0) {
if (VERSION.SDK_INT >= 23) {
this.onApplyThemeResource(this.getTheme(), this.mThemeId, false);
} else {
this.setTheme(this.mThemeId);
}
}
super.onCreate(savedInstanceState);
}
installViewFactory方法
public void installViewFactory() {
LayoutInflater layoutInflater = LayoutInflater.from(this.mContext);
if (layoutInflater.getFactory() == null) {
LayoutInflaterCompat.setFactory2(layoutInflater, this);
}
}
public final View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
return this.createView(parent, name, context, attrs);
}
createView方法
public View createView(View parent, String name, @NonNull Context context, @NonNull AttributeSet attrs) {
...
//创建 mAppCompatViewInflater对象
return this.mAppCompatViewInflater.createView(parent, name, context, attrs, inheritContext, IS_PRE_LOLLIPOP, true, VectorEnabledTintResources.shouldBeUsed());
}
3.3 拦截View替换的 AppCompatViewInflater createView 源码
final View createView(View parent, String name, @NonNull Context context, @NonNull AttributeSet attrs, boolean inheritContext, boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
Context originalContext = context;
if (inheritContext && parent != null) {
context = parent.getContext();
}
if (readAndroidTheme || readAppTheme) {
context = themifyContext(context, attrs, readAndroidTheme, readAppTheme);
}
if (wrapContext) {
context = TintContextWrapper.wrap(context);
}
View view = null;
byte var12 = -1;
switch(name.hashCode()) {
case -1946472170:
if (name.equals("RatingBar")) {
var12 = 11;
}
break;
case -1455429095:
if (name.equals("CheckedTextView")) {
var12 = 8;
}
break;
case -1346021293:
if (name.equals("MultiAutoCompleteTextView")) {
var12 = 10;
}
break;
case -938935918:
if (name.equals("TextView")) {
var12 = 0;
}
break;
case -937446323:
if (name.equals("ImageButton")) {
var12 = 5;
}
break;
case -658531749:
if (name.equals("SeekBar")) {
var12 = 12;
}
break;
case -339785223:
if (name.equals("Spinner")) {
var12 = 4;
}
break;
case 776382189:
if (name.equals("RadioButton")) {
var12 = 7;
}
break;
case 1125864064:
if (name.equals("ImageView")) {
var12 = 1;
}
break;
case 1413872058:
if (name.equals("AutoCompleteTextView")) {
var12 = 9;
}
break;
case 1601505219:
if (name.equals("CheckBox")) {
var12 = 6;
}
break;
case 1666676343:
if (name.equals("EditText")) {
var12 = 3;
}
break;
case 2001146706:
if (name.equals("Button")) {
var12 = 2;
}
}
switch(var12) {
case 0:
view = this.createTextView(context, attrs);
this.verifyNotNull((View)view, name);
break;
case 1:
view = this.createImageView(context, attrs);
this.verifyNotNull((View)view, name);
break;
case 2:
view = this.createButton(context, attrs);
this.verifyNotNull((View)view, name);
break;
case 3:
view = this.createEditText(context, attrs);
this.verifyNotNull((View)view, name);
break;
case 4:
view = this.createSpinner(context, attrs);
this.verifyNotNull((View)view, name);
break;
case 5:
view = this.createImageButton(context, attrs);
this.verifyNotNull((View)view, name);
break;
case 6:
view = this.createCheckBox(context, attrs);
this.verifyNotNull((View)view, name);
break;
case 7:
view = this.createRadioButton(context, attrs);
this.verifyNotNull((View)view, name);
break;
case 8:
view = this.createCheckedTextView(context, attrs);
this.verifyNotNull((View)view, name);
break;
case 9:
view = this.createAutoCompleteTextView(context, attrs);
this.verifyNotNull((View)view, name);
break;
case 10:
view = this.createMultiAutoCompleteTextView(context, attrs);
this.verifyNotNull((View)view, name);
break;
case 11:
view = this.createRatingBar(context, attrs);
this.verifyNotNull((View)view, name);
break;
case 12:
view = this.createSeekBar(context, attrs);
this.verifyNotNull((View)view, name);
break;
default:
view = this.createView(context, name, attrs);
}
if (view == null && originalContext != context) {
view = this.createViewFromTag(context, name, attrs);
}
if (view != null) {
this.checkOnClickListener((View)view, attrs);
}
return (View)view;
}
到此为止,大家应该清楚contentView的流程了吧,不清楚的自己可以再走一边源码
通过上面的源码浏览 大家应该知道如何去 通过Factory拦截View,进行对View的二次加工
LayoutInflater.from(this).setFactory(new LayoutInflater.Factory() {
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
return null;
}
});