您的位置:首页 > 移动开发 > Android开发

Android视图加载到窗体的流程分析

2016-06-26 23:31 435 查看

Android视图加载到窗体的流程分析

ONE Goal ,ONE Passion !


每天与界面打交道,也没有真正去整理过activity的界面时如何加载到窗体上的.扒扒代码看看系统是如何帮我们做的吧.

想知道布局视图加载到手机屏幕上的流程,我们首先要从acticity中的setContentView方法开始分析.

第一:执行activity中的setContentView().我们看一下它到底做了什么

/**
* 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);
initWindowDecorActionBar();
}


第二: 可以看到真正执行的是getWindow().setContentView(layoutResID);

2.1:那么getWindow()到底是什么鬼.

/**
* Retrieve the current {@link android.view.Window} for the activity.
* This can be used to directly access parts of the Window API that
* are not available through Activity/Screen.
*
* @return Window The current window, or null if the activity is not
*         visual.
*/

private Window mWindow;

public Window getWindow() {
return mWindow;
}


2.2:咦.mWindow怎么来的呢?Activit中的main方法中会调用attach.随之就产出了一个mWindow对象.我们的mWindow对象实际上是一个PhoneWindow.

final void attach(...){
mWindow = new PhoneWindow(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);

....
}


2.3: 拿到了mWindow后,看看它有做了什么

/**
* Convenience for
* {@link #setContentView(View, android.view.ViewGroup.LayoutParams)}
* to set the screen content from a layout resource.  The resource will be
* inflated, adding all top-level views to the screen.
*
* @param layoutResID Resource ID to be inflated.
* @see #setContentView(View, android.view.ViewGroup.LayoutParams)
*/

将一个布局资源文件中设置为screen content(屏幕内容),这个资源将被填充到这个screen的顶层view中.
public abstract void setContentView(@LayoutRes int layoutResID);


2.4可以看到getWindow().setContentView(layoutResID);实际上是PhoneWindow.setContentWindow.

PhoneWindow中的setContentWindow();

public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent);// 将layoutResID填充,他的父View是mContentParent是在installDecor方法里面mContentParent = generateLayout(mDecor);得到的
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}


执行到installDecor()

private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();//生成装饰窗口,装饰窗口继承自FrameLayout
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);// 产生布局,返回父布局,暂时这样理解,具体进去看代码
mDecor.makeOptionalFitsSystemWindows();
mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
......
}
}
}


generateLayout的代码如下

protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.

TypedArray a = getWindowStyle();// 获取当前设置的主题
......
if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) {
requestFeature(FEATURE_NO_TITLE);//可以看到平时在AndroidManifest配置的窗口等各其实在代码里都是在这里修改的
} else if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBar, false)) {
// Don't allow an action bar if there is no title.
requestFeature(FEATURE_ACTION_BAR);
}

if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBarOverlay, false)) {
requestFeature(FEATURE_ACTION_BAR_OVERLAY);
}

//19-63行根据我们指定的有无标题等各种窗口风格得到对应的默认布局,
//这些布局在android4.2-source\frameworks\base\core\res\res\layout

int layoutResource;
int features = getLocalFeatures();
if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = com.android.internal.R.layout.screen_title_icons;
}
removeFeature(FEATURE_ACTION_BAR);
} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
&& (features & (1 << FEATURE_ACTION_BAR)) == 0) {
layoutResource = com.android.internal.R.layout.screen_progress;
} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = com.android.internal.R.layout.screen_custom_title;
}
removeFeature(FEATURE_ACTION_BAR);
} else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
com.android.internal.R.attr.dialogTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
if ((features & (1 << FEATURE_ACTION_BAR_OVERLAY)) != 0) {
layoutResource = com.android.internal.R.layout.screen_action_bar_overlay;
} else {
layoutResource = com.android.internal.R.layout.screen_action_bar;
}
} else {
layoutResource = com.android.internal.R.layout.screen_title;
}
} else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode;
} else {
layoutResource = com.android.internal.R.layout.screen_simple;
}

mDecor.startChanging();

View in = mLayoutInflater.inflate(layoutResource, null);//根据上面的判断选择的layoutResource填充成View
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));//调用装饰窗口的addView方法将上一步生成的View添加到最外层的装饰窗口

ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);//ID_ANDROID_CONTENT对应的都是@android:id/content其实是一个FrameLayout
......
mDecor.finishChanging();
return contentParent;
}


这里已经完成了对布局文件的加载.

第二:再看看initWindowDecorActionBar();

/**
* Creates a new ActionBar, locates the inflated ActionBarView,
* initializes the ActionBar with the view, and sets mActionBar.
*/

创建一个新的ActionBar,去放置在ActionBarView中.将ActionBar初始化一个view.

private void initWindowDecorActionBar() {
Window window = getWindow();

// Initializing the window decor can change window feature flags.
// Make sure that we have the correct set before performing the test below.
window.getDecorView();

if (isChild() || !window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) {
return;
}

mActionBar = new WindowDecorActionBar(this);
mActionBar.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp);

mWindow.setDefaultIcon(mActivityInfo.getIconResource());
mWindow.setDefaultLogo(mActivityInfo.getLogoResource());
}


第三:看一下screen两种常见布局:

默认screen布局:

android4.2-source\frameworks\base\core\res\res\layout文件夹下Screen-title.xml

<linearlayout android:fitssystemwindows="true" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Popout bar for action modes -->
<viewstub android:id="@+id/action_mode_bar_stub" android:inflatedid="@+id/action_mode_bar" android:layout="@layout/action_mode_bar" android:layout_height="wrap_content" android:layout_width="match_parent">
<frameLayout
android:layout_width="match_parent"
android:layout_height="?android:attr/windowTitleSize"
style="?android:attr/windowTitleBackgroundStyle">
<textview android:background="@null" android:fadingedge="horizontal" android:gravity="center_vertical" android:id="@android:id/title" android:layout_height="match_parent" android:layout_width="match_parent" style="?android:attr/windowTitleStyle">
</frameLayout>
<frameLayout android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</viewstub></linearlayout>


设置全屏时加载的screen布局

<linearlayout android:fitssystemwindows="true" android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android">
<viewstub android:id="@+id/action_mode_bar_stub" android:inflatedid="@+id/action_mode_bar" android:layout="@layout/action_mode_bar" android:layout_height="wrap_content" android:layout_width="match_parent">
<frameLayout
android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foregroundInsidePadding="false"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</viewstub></linearlayout>


第4: 我们设置布局时使用的setConentView,为什么是setContent呢?其实就是将资源文件加载到了content中.

最后总结一下:我们设置setContentView时关键就是执行了下面两行代码

//将布局填充到Content中
getWindow().setContentView(layoutResID);
//创建 ActionBar.
initWindowDecorActionBar();


要睡觉了.(~﹃~)~zZ
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 界面