AndroidUI绘制流程及原理
一 布局加载
setContentView(R.layout.layout);
我们平时写好Layout布局,只需调用 setContentView(R.layout.layout);
这个方法就可以把写的页面显示在屏幕上,那么这个方法背后到底做了些什么了?我们看看源码,定位到 setContentView
这个方法的源码
定位到Activity
的setContentView
方法中
getWindow().setContentView(layoutResID);
通过调用 getWindow().setContentView(layoutResID);
这个方法将我们的布局ID传了进去
我们看看 getWindow()
得到了什么?
public Window getWindow() {
return mWindow;
}
它返回的是一个mWindow
对象,那么mWindow
又是什么了?
* <p>The only existing implementation of this abstract class is
* android.view.PhoneWindow, which you should instantiate when needing a
* Window.
大概意思 PhoneWindow 是 Window 的唯一子类
cpp
public abstract class Window {
/** Flag for the "options panel" feature. This is enabled by default. */
public static final int FEATURE_OPTIONS_PANEL = 0;
/** Flag for the "no title" feature, turning off the title at the top
* of the screen. */
public static final int FEATURE_NO_TITLE = 1;
可以看到 mWindow
是一个抽象类的对象,而 PhoneWindow 是它的子类,所以通过源码我们知道 mWindow
实际上就是 PhoneWindow
的一个对象而已
那么 getWindow().setContentView(layoutResID);
实际上就是调用 PhoneWindow
里面的 setContentView(layoutResID)
我们来到PhoneWindow
类里面的 setContentView(int layoutResID)
方法,看看它做了什么?
@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();
} 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;
}
首先会执行 installDecor();
这个方法
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {//判断 DecorView 是否为空 为空的话 就调用 generateDecor(-1) 创建一个
//DecorView 其实是我们界面窗口的 顶层容器 ,下面会有介绍及插图
mDecor = generateDecor(-1);// 重点一
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);// 重点二
// Set up decor part of UI to ignore fitsSystemWindows if appropriate.
mDecor.makeOptionalFitsSystemWindows();
定位到generateDecor(-1);
方法里面
protected DecorView generateDecor(int featureId) {
// System process doesn't have application context and in that case we need to directly use
// the context we have. Otherwise we want the application context, so we don't cling to the
// activity.
Context context;
if (mUseDecorContext) {
Context applicationContext = getContext().getApplicationContext();
if (applicationContext == null) {
context = getContext();
} else {
context = new DecorContext(applicationContext, getContext());
if (mTheme != -1) {
context.setTheme(mTheme);
}
}
} else {
context = getContext();
}
return new DecorView(context, featureId, this, getAttributes());
}
我们可以看到 它是直接 new DecorView
就返回回去了
我们又回到 installDecor();
方法中的 mContentParent = generateLayout(mDecor)
;这行代码
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.//应用当前主题的数据
TypedArray a = getWindowStyle();
if (false) {
System.out.println("From style:");
String s = "Attrs:";
for (int i = 0; i < R.styleable.Window.length; i++) {
s = s + " " + Integer.toHexString(R.styleable.Window[i]) + "="
+ a.getString(i);
}
System.out.println(s);
}
mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
& (~getForcedWindowFlags());
if (mIsFloating) {
setLayout(WRAP_CONTENT, WRAP_CONTENT);
setFlags(0, flagsToUpdate);
} else {
setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
}
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()));
}
int layoutResource;
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)
.........
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);//重点代码一
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); //重点代码二
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
........................
以上代码的大概意思就是 设置主题属性 及 得到一个 系统的 初始化容器 layoutResource = R.layout.screen_title_icons;
当上面的一些主题及属性及相对应的 初始化容器 layoutResource = R.layout.screen_title_icons完成后;
就会执行这行代码
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);//重点代码一
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {//把得到的layoutResource 就是上面我们得到的系统Layout 传进去
if (mBackdropFrameRenderer != null) {
loadBackgroundDrawablesIfNeeded();
mBackdropFrameRenderer.onResourcesLoaded(
this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
getCurrentColor(mNavigationColorViewState));
}
mDecorCaptionView = createDecorCaptionView(inflater);
final View root = inflater.inflate(layoutResource, null);//解析成一个view
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));//然后添加到DecorView上面
}
mContentRoot = (ViewGroup) root;
initializeElevation();
}
执行
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); //重点代码二
根据源码我们可以知道 上面的 layoutResource
(screen_simple.xml)它其实是一个线性布局而已
findViewById
就是得到这个线性布局里面的一个子容器(下图FrameLayout就是这个子容器)
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
//FrameLayout 这个容器就是 我们得到的 contentParent
到这里 installDecor()
这个方法就完成了,走到这里我们来梳理一下
第一步 :创建 DecorView 顶层容器
第二步: 向DecorView 里面添加一个系统的布局容器 (就是screen_simple,xml 它是一个线性布局)
第三步: 得到系统布局容器里面的 子容器 (FrameLayout)
第四步: 子容器(FrameLayout)就可以添加我们自己的 xml布局了
installDecor()
方法中的 mContentParent
就是 子容器
上一篇: Android UI绘制流程之测量片
下一篇: Android高级UI之绘制流程分析