以下源码基于android 12.0.0_r3
// Activity.java
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback,
AutofillManager.AutofillClient, ContentCaptureManager.ContentCaptureClient {
...
@UnsupportedAppUsage
private Window mWindow; // 其唯一实现类 -> PhoneWindow.java
/**
* Set the activity content from a layout resource. The resource will be
* inflated, adding all top-level views to the activity.
*
* @param layoutResID Resource ID to be inflated.
*
* @see #setContentView(android.view.View)
* @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
*/
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID); // 调到PhoneWindow#setContentView()
...
}
...
}
// PhoneWindow.java
/**
* Android-specific Window.
* <p>
* todo: need to pull the generic functionality out into a base class
* in android.widget.
*
* @hide
*/
public class PhoneWindow extends Window implements MenuBuilder.Callback {
...
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;
// This is the view in which the window contents are placed. It is either
// mDecor itself, or a child of mDecor where the contents go.
ViewGroup mContentParent;
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor();
}
...
// 解析外部传入的layoutResID,并将mContentParent作为解析布局的根布局
mLayoutInflater.inflate(layoutResID, mContentParent);
...
}
private void installDecor() {
if (mDecor == null) {
// 创建DecorView
mDecor = generateDecor(-1);
...
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
// 创建ContentParent(ViewGroup),用于作为外部布局的容器使用
mContentParent = generateLayout(mDecor);
...
}
protected ViewGroup generateLayout(DecorView decor) {
// 1. 根据当前主题设置decor样式
// Apply data from current theme.
// 代码省略
...
// 2. 渲染decor
// Inflate the window decor.
int layoutResource;
int features = getLocalFeatures();
// 3. 根据features获取不同的title布局,比如FEATURE_NO_TITLE,FEATURE_ACTION_BAR,FEATURE_ACTION_MODE_OVERLAY
// 代码省略
...
// 通知DecorView 根据titile布局初始化mContentRoot
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
...
return contentParent;
}
}
// DecorView.java
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
...
// Action bar
private ActionBarContextView mPrimaryActionModeView; // 通过ViewStub懒加载 R.id.action_mode_bar_stub
@UnsupportedAppUsage
private PhoneWindow mWindow;
ViewGroup mContentRoot;
/**
* 初始化mContentRoot。在PhoneWindow.generateLayout中调用
*/
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
...
// 根据PhoneWindow.generateLayout中获取的title布局id渲染布局
final View root = inflater.inflate(layoutResource, null);
...
// 将生成的root添加到DecorView中作为根布局
// Put it below the color views.
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) root;
}
@Override
public void onDraw(Canvas c) {
super.onDraw(c);
// 将mWindow.mContentParent绘制到mContentRoot上
/**
* Draws the fallback background.
*
* @param boundsView The view determining with which bounds the background should be drawn.
* @param root The view group containing the content.
* @param c The canvas to draw the background onto.
* @param content The view where the actual app content is contained in.
* @param coveringView1 A potentially opaque view drawn atop the content
* @param coveringView2 A potentially opaque view drawn atop the content
*/
mBackgroundFallback.draw(this, mContentRoot, c, mWindow.mContentParent,
mStatusColorViewState.view, mNavigationColorViewState.view);
}
...
}
-
Activity.attach()
: 创建mWindow = new PhoneWindow()
-
Activity.setContentView()
: 调用PhoneWindow.setContentView()
做了以下两件事:
- 通过
PhoneWindow.installDecor()
初始化DecorView和ContentParent(ViewGroup,com.android.internal.R.id.content)。 - 使用
mLayoutInflater.inflate(layoutResID, mContentParent)
将ContentView添加到mContentParent
中,但此时mContentParent
还只是一个游离的ViewGroup。
-
DecorView.onDraw()
:将mWindow.mContentParent
绘制到mContentRoot
上。
以上,Activity的界面构成就已经清晰了:
Activity结构图
网友评论