Activity与AppCompatActivity的setContentView的源码分析
Activity 中的MainActivity extends AppcompatActivity 与 extends Activity的区别呢?
解答: AppCompatActivity是继承自v4的FragmentAvtivity,并且加入了很多的新特性。这个可以很好的兼容老设备 且在AppCompatActivity和Activity的区别在于app运行后是否有ActionBar的区别,界面(appname显示或者不显示)。对程序本身没有什么影响。
首先是我们打开一个Android应用,映入眼帘的是MainActivity extends AppcompatActivty,这是现在比较高版本的Android studio的创建的方式。或许你们见MainActivity extends Activity的版本,今天我们就是关于两者的setContextView方法进行讲解。
这篇文章我们先从Activity的方式进行讲解。
首先我们看到Activity的setContentView方法源码
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
由于上面的源码得到,setContentView是移交给了getWindow返回的对象进行setContentView方法的实现。然而getWindow返回的对象是什么呢?这不经让我们好奇!! 我们再去查看源码:
有以上得知,PhoneWindow是Window的唯一实现类。所以实现setContentView方法的是PhoneWindow,所以我们再去打开PhoneWindow的setContentView方法:(我截取了关键性代码)
@Override
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 {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
在以上代码中是否又有很多的疑惑呢?现在我来跟你慢慢来讲解。
我们首先看到的是一个判断语句,在语句里面我们会发现几个陌生的对象(对于想我这种小白来说是这样的):mContentParent 以及几个陌生的方法 installDecor removeAllViews,首先mContentParent是什么呢?其次installDecor removeAllViews方法有么作用呢? 在这我直接公布答案:mContentParent就是自己调用setContentView方法中传进来的那个的布局(是一个ViewGroup) 而installDecor是在判断mContentParent为空时候,去新建一个DecorView 我们之后会得知DecorView继承自FrameLayout(也是个ViewGroup)并且是Activity的根布局,为什么mContentParent为空建造DecorView呢?这就联想到mContentParent其实是嵌套在了DecorView中的一个子View。
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
}
}
在代码中我们首先是判断mDecor是否为空(mDecor其实就是DecorView),就通过generateDecor方法生成一个DecorView接着就去generateLayout去生成一个mContentParent,所以我们去紧接着去看generateLayout去看一下怎么去生成mContentParent的
protected ViewGroup generateLayout(DecorView decor) {
TypedArray a = getWindowStyle();
int layoutResource;
int features = getLocalFeatures();
else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
&& (features & (1 << FEATURE_ACTION_BAR)) == 0) {
layoutResource = R.layout.screen_progress;
} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
layoutResource = R.layout.screen_custom_title;
} else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
layoutResource = res.resourceId;
layoutResource = R.layout.screen_title;
} else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
layoutResource = R.layout.screen_simple_overlay_action_mode;
} else {
layoutResource = R.layout.screen_simple;
}
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
mDecor.finishChanging();
return contentParent;
}
generateLayout()方法去根据Theme和style获取一个系统的布局LayoutResourse
在根据这个LayoutResourse去调用mDecor.onResoursesLoaded(mLayoutinflater,LayouResourse);将这个布局加载到mDecor中,之后通过findViewById方法获取到contentParent对象(其实是mContentParent)
我们点进findViewById方法:
@Nullable
public <T extends View> T findViewById(@IdRes int id) {
return getDecorView().findViewById(id);
}```
这只是方法,我们还看见了出入了一个参数ID_ANDROID_CONTENT是什么呢?点进去看看:
```java
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
这就是说通过DecorView的find ViewById方法找到了com.android.internal.R.id.content的contentParent对象,这样也佐证了mContentParent是嵌套在mDecor中的。
我们再去回到最上面的generateLayout方法中,最后将contentParent返回,这样instalDecor方法就分析完了。这样就是DecorView和ContentParent都创建好了。
我们再回到PhoneWindow中的setContentView方法中,我们会看到一个代码
@Override
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 {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
上面的mLayoutInflater.inflate(layoutResID, mContentParent);现在我们就知mContentParent就是上面mDecor创建返回的子view。而另外的参数是什么?很显然是我们层层传来的布局就好比MainActivity传来的R.layout.activity_main传入我们自己的布局并且加载到mContentView中去。以上就是Activity通过setContentView加载视图的过程:我们来总结一下:
图中水平箭头表示的是右面框里的内容是在左面框方法的内部执行的,竖直箭头表示下面的框是在上面框中方法执行结束后执行的。下面这张图展示了布局加载的流程:
以上就是我对Activity使用setContentView加载视图的理解。部分地方引用了其他资源,仅供大家学习,不做商用。欢迎大家来进行指正。
|