上篇文章是App的启动的流程这篇学习Activity中的布局文件是如何绘制的。先从onCreate的setContentView开始。先看下Activity的绘制过程
##Activity
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);//getWindow拿到的是PhoneWindow
initWindowDecorActionBar();
}
##PhoneWindow
public void setContentView(int layoutResID) {
...
if (mContentParent == null) {
installDecor();//初始化DecorView
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
...
}
private void installDecor() {//导航栏什么的在这个方法中设置
...
mContentParent = generateDecor(-1);//创建DecorView
...
mContentParent = generateLayout(mDecor);//设置DecorView布局
...
}
protected DecorView generateDecor(int featureId) {
...
return new DecorView(context, featureId, this, getAttributes());
}
protected ViewGroup generateLayout(DecorView decor) {
// 获取AndroidManifest.xml中指定的themes主题
TypedArray a = getWindowStyle();
...
if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
requestFeature(FEATURE_NO_TITLE);
} else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) { // Don't allow an action bar if there is no title. requestFeature(FEATURE_ACTION_BAR);
} if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) {
requestFeature(FEATURE_ACTION_BAR_OVERLAY);
} if (a.getBoolean(R.styleable.Window_windowActionModeOverlay, false)) {
requestFeature(FEATURE_ACTION_MODE_OVERLAY);
} if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) {
requestFeature(FEATURE_SWIPE_TO_DISMISS);
} if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {
setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
} if (a.getBoolean(R.styleable.Window_windowTranslucentStatus, false)) {
setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS & (~getForcedWindowFlags()));
} if (a.getBoolean(R.styleable.Window_windowTranslucentNavigation, false)) {
setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION & (~getForcedWindowFlags()));
} if (a.getBoolean(R.styleable.Window_windowOverscan, false)) {
setFlags(FLAG_LAYOUT_IN_OVERSCAN, FLAG_LAYOUT_IN_OVERSCAN&(~getForcedWindowFlags()));
} if (a.getBoolean(R.styleable.Window_windowShowWallpaper, false)) {
setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));
} if (a.getBoolean(R.styleable.Window_windowEnableSplitTouch, getContext().getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.HONEYCOMB)) {
setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags()));
}
a.getValue(R.styleable.Window_windowMinWidthMajor, mMinWidthMajor);
a.getValue(R.styleable.Window_windowMinWidthMinor, mMinWidthMinor);
if (DEBUG) Log.d(TAG, "Min width minor: " + mMinWidthMinor.coerceToString() + ", major: " + mMinWidthMajor.coerceToString());
if (a.hasValue(R.styleable.Window_windowFixedWidthMajor)) {
if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue(); a.getValue(R.styleable.Window_windowFixedWidthMajor, mFixedWidthMajor);
} if (a.hasValue(R.styleable.Window_windowFixedWidthMinor)) {
if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue(); a.getValue(R.styleable.Window_windowFixedWidthMinor, mFixedWidthMinor);
} if (a.hasValue(R.styleable.Window_windowFixedHeightMajor)) {
if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue(); a.getValue(R.styleable.Window_windowFixedHeightMajor, mFixedHeightMajor);
} if (a.hasValue(R.styleable.Window_windowFixedHeightMinor)) {
if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue(); a.getValue(R.styleable.Window_windowFixedHeightMinor, mFixedHeightMinor);
} if (a.getBoolean(R.styleable.Window_windowContentTransitions, false)) {
requestFeature(FEATURE_CONTENT_TRANSITIONS);
} if (a.getBoolean(R.styleable.Window_windowActivityTransitions, false)) {
requestFeature(FEATURE_ACTIVITY_TRANSITIONS);
} mIsTranslucent = a.getBoolean(R.styleable.Window_windowIsTranslucent, false);
...
if (!mForcedStatusBarColor) {
mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000);
} if (!mForcedNavigationBarColor) {
mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000); mNavigationBarDividerColor = a.getColor(R.styleable.Window_navigationBarDividerColor, 0x00000000);
}
...
int layoutResource; //DecorView 的布局文件
int features = getLocalFeatures();
// System.out.println("Features: 0x" + Integer.toHexString(features));
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
layoutResource = R.layout.screen_swipe_dismiss; setCloseOnSwipeEnabled(true);
} else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
if (mIsFloating) {
TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( R.attr.dialogTitleIconsDecorLayout, res, true); layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_title_icons;
}
// XXX Remove this once action bar supports these features. removeFeature(FEATURE_ACTION_BAR);
// System.out.println("Title Icons!");
} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0 && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
// Special case for a window with only a progress bar (and title).
// XXX Need to have a no-title version of embedded windows. layoutResource = R.layout.screen_progress;
// System.out.println("Progress!");
} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
// Special case for a window with a custom title.
// If the window is floating, we need a dialog layout
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute( R.attr.dialogCustomTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_custom_title;
}
// XXX Remove this once action bar supports these
features. removeFeature(FEATURE_ACTION_BAR);
} else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
// If no other features and not embedded, only need a title.
// If the window is floating, we need a dialog layout
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute( R.attr.dialogTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
layoutResource = a.getResourceId( R.styleable.Window_windowActionBarFullscreenDecorLayout, R.layout.screen_action_bar);
} else {
layoutResource = R.layout.screen_title;
}
// System.out.println("Title!");
} else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
layoutResource = R.layout.screen_simple_overlay_action_mode;//DecorView 的布局文件配置
} else {
// Embedded, so no decoration is needed.
layoutResource = R.layout.screen_simple;//DecorView 的布局文件配置
// System.out.println("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");
} if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
ProgressBar progress = getCircularProgressBar(false);
if (progress != null) { progress.setIndeterminate(true);
}
} if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
registerSwipeCallbacks(contentParent);
}
...
return contentParent;
}
##R.layout.screen_simple_overlay_action_mode.xml
android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> android:layout_width="match_parent" android:layout_height="match_parent" android:foregroundInsidePadding="false" android:foregroundGravity="fill_horizontal|top" android:foreground="?android:attr/windowContentOverlay" /> android:inflatedId="@+id/action_mode_bar" android:layout="@layout/action_mode_bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="?attr/actionBarTheme" /> ##R.layout.screen_simple.xml android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:orientation="vertical"> android:id="@+id/action_mode_bar_stub" android:inflatedId="@+id/action_mode_bar" android:layout="@layout/action_mode_bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="?attr/actionBarTheme" /> android:layout_width="match_parent" android:layout_height="match_parent" android:foregroundInsidePadding="false" android:foregroundGravity="fill_horizontal|top" android:foreground="?android:attr/windowContentOverlay" /> DecorView的布局文件可以看出其实我们写的布局文件其实是通过id为content的布局进行添加的 那接下来看看是如何解析我们写的布局的 ##DecorView void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {//将View树 ... 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(); } private DecorCaptionView createDecorCaptionView(LayoutInflater inflater) {//DecorCaptionView 布局文件在这个方法中添加 DecorCaptionView decorCaptionView = null; for (int i = getChildCount() - 1; i >= 0 && decorCaptionView == null; i--) { ... decorCaptionView = inflateDecorCaptionView(inflater); ... return decorCaptionView; } private DecorCaptionView inflateDecorCaptionView(LayoutInflater inflater) { final Context context = getContext(); // We make a copy of the inflater, so it has the right context associated with it. inflater = inflater.from(context); final DecorCaptionView view = (DecorCaptionView) inflater.inflate(R.layout.decor_caption, null); setDecorCaptionShade(context, view); return view; } ##R.layout.decor_caption.xml xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:descendantFocusability="beforeDescendants" > android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="end" android:background="@drawable/decor_caption_title" android:focusable="false" android:descendantFocusability="blocksDescendants" > 这里是将view树这里也是将整个窗口的内容添加到DecorView 的根目录包括title和content,并将整个窗口内容赋值给mContentRoot Activity的绘制流程到这里就结束了接下来看看AppCampatActivity。同样从setContentView开始 ##AppCompatActivity 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; } ##AppCompatDelegate public abstract void setContentView(@LayoutRes int var1); ##AppCompatDelegateImpl public void setContentView(int resId) { this.ensureSubDecor();//创建DecorView 和 ViewGroup //获取id为16908290的容器 ViewGroup contentParent = (ViewGroup)this.mSubDecor.findViewById(16908290); contentParent.removeAllViews(); //通过布局加载器解析自己写的xml布局文件转换为View树,并把解析出来的View树添加到contentParent(ViewGroup)布局容器中 LayoutInflater.from(this.mContext).inflate(resId, contentParent); this.mOriginalWindowCallback.onContentChanged(); } private void ensureSubDecor() { if (!this.mSubDecorInstalled) { this.mSubDecor = this.createSubDecor(); CharSequence title = this.getTitle();//设置title if (!TextUtils.isEmpty(title)) { if (this.mDecorContentParent != null) { this.mDecorContentParent.setWindowTitle(title); } else if (this.peekSupportActionBar() != null) { this.peekSupportActionBar().setWindowTitle(title); } else if (this.mTitleView != null) { this.mTitleView.setText(title); } } ... } } private ViewGroup createSubDecor() { ... this.mWindow.getDecorView(); LayoutInflater inflater = LayoutInflater.from(this.mContext); ViewGroup subDecor = null; if (!this.mWindowNoTitle) {//根据主题加载不同布局 if (this.mIsFloating) { subDecor = (ViewGroup)inflater.inflate(layout.abc_dialog_title_material, (ViewGroup)null); this.mHasActionBar = this.mOverlayActionBar = false; } else if (this.mHasActionBar) { ... subDecor = (ViewGroup)LayoutInflater.from((Context)themedContext).inflate(layout.abc_screen_toolbar, (ViewGroup)null); ... } } else { if (this.mOverlayActionMode) { subDecor = (ViewGroup)inflater.inflate(layout.abc_screen_simple_overlay_action_mode, (ViewGroup)null); } else { subDecor = (ViewGroup)inflater.inflate(layout.abc_screen_simple, (ViewGroup)null); } ... } if (subDecor == null) { throw new IllegalArgumentException("AppCompat does not support the current theme features: { windowActionBar: " + this.mHasActionBar + ", windowActionBarOverlay: " + this.mOverlayActionBar + ", android:windowIsFloating: " + this.mIsFloating + ", windowActionModeOverlay: " + this.mOverlayActionMode + ", windowNoTitle: " + this.mWindowNoTitle + " }"); } else { if (this.mDecorContentParent == null) { this.mTitleView = (TextView)subDecor.findViewById(id.title); } ViewUtils.makeOptionalFitsSystemWindows(subDecor);//这里用放射调用View的makeOptionalFitsSystemWindows方法该方法为设置Flag ContentFrameLayout contentView = (ContentFrameLayout)subDecor.findViewById(id.action_bar_activity_content); ViewGroup windowContentView = (ViewGroup)this.mWindow.findViewById(16908290); if (windowContentView != null) { while(windowContentView.getChildCount() > 0) { View child = windowContentView.getChildAt(0); windowContentView.removeViewAt(0); contentView.addView(child); } windowContentView.setId(-1); contentView.setId(16908290); if (windowContentView instanceof FrameLayout) { ((FrameLayout)windowContentView).setForeground((Drawable)null); } } this.mWindow.setContentView(subDecor);//这里mWindow的setContentView具体实现为PhoneWindow的setContentView ... return subDecor; } } } public void setContentView(View view, ViewGroup.LayoutParams params) { // 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();//这里和activity的调用是一样的就不继续了 } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); } if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { view.setLayoutParams(params); final Scene newScene = new Scene(mContentParent, view); transitionTo(newScene); } else { mContentParent.addView(view, params); } mContentParent.requestApplyInsets(); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } mContentParentExplicitlySet = true; } AppComapActivity只是在Acitivity的installDecor()调用逻辑之前多了一些配置支持Material design风格 到这里UI绘制流程就结束了。 备注:文中Android源码版本9.0 作者:Dean_Xu 原创博客,请注明转载处....