Activity的setContentView的流程
最简单的Activity中的内容大致是这样的:
public class MainActivity extends Activity { @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
} }
setContentView
一般来说我们设置页面的内容视图是都是通过setContentView方法,那么我们就以2.3源码为例就来看看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.
*/
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
} public Window getWindow() {
return mWindow;
} private Window mWindow;
我们可以看到,实际上调用的mWindow的setContentView方法,在Android Touch事件分发过程这篇文章中我们已经指出Window的实现类为PhoneWindow类
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor(); // 1、生成DecorView
} else {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent);// 2、将layoutResId的布局添加到mContentParent中
final Callback cb = getCallback();
if (cb != null) {
cb.onContentChanged();
}
}
// 构建mDecor对象,并且初始化标题栏和Content Parent(我们要显示的内容区域)
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor(); // 3、构建DecorView
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor); // 4、获取ContentView容器,即显示内容的区域 mTitleView = (TextView)findViewById(com.android.internal.R.id.title); 、设置Title等
if (mTitleView != null) {
if ((getLocalFeatures() & ( << FEATURE_NO_TITLE)) != ) {
View titleContainer = findViewById(com.android.internal.R.id.title_container);
if (titleContainer != null) {
titleContainer.setVisibility(View.GONE);
} else {
mTitleView.setVisibility(View.GONE);
}
if (mContentParent instanceof FrameLayout) {
((FrameLayout)mContentParent).setForeground(null);
}
} else {
mTitleView.setText(mTitle);
}
}
}
} protected DecorView generateDecor() {
return new DecorView(getContext(), -); // 构建mDecor对象
}
我们可以看到,setContentView的基本流程简单概括就是如下几步:
1、构建mDecor对象。mDecor就是整个窗口的顶层视图,它主要包含了Title和Content View两个区域 (参考图1中的两个区域 ),Title区域就是我们的标题栏,Content View区域就是显示我们xml布局内容中的区域。关于mDecor对象更多说明也请参考Android Touch事件分发过程这篇文章;
2、设置一些关于窗口的属性,初始化标题栏区域和内容显示区域;
这里比较复杂的就是generateLayout(mDecor)这个函数,我们一起来分析一下吧。
// 返回用于显示我们设置的页面内容的ViewGroup容器
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
// 1、获取窗口的Style属性
TypedArray a = getWindowStyle(); if (false) {
System.out.println("From style:");
String s = "Attrs:";
for (int i = ; i < com.android.internal.R.styleable.Window.length; i++) {
s = s + " " + Integer.toHexString(com.android.internal.R.styleable.Window[i]) + "="
+ a.getString(i);
}
System.out.println(s);
}
// 窗口是否是浮动的
mIsFloating = a.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, false);
int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
& (~getForcedWindowFlags());
if (mIsFloating) {
setLayout(WRAP_CONTENT, WRAP_CONTENT);
setFlags(, flagsToUpdate);
} else {
setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
}
// 设置是否不显示title区域
if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) {
requestFeature(FEATURE_NO_TITLE);
}
// 设置全屏的flag
if (a.getBoolean(com.android.internal.R.styleable.Window_windowFullscreen, false)) {
setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN&(~getForcedWindowFlags()));
} if (a.getBoolean(com.android.internal.R.styleable.Window_windowShowWallpaper, false)) {
setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));
} WindowManager.LayoutParams params = getAttributes();
// 设置输入法模式
if (!hasSoftInputMode()) {
params.softInputMode = a.getInt(
com.android.internal.R.styleable.Window_windowSoftInputMode,
params.softInputMode);
} if (a.getBoolean(com.android.internal.R.styleable.Window_backgroundDimEnabled,
mIsFloating)) {
/* All dialogs should have the window dimmed */
if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == ) {
params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
}
params.dimAmount = a.getFloat(
android.R.styleable.Window_backgroundDimAmount, 0.5f);
}
// 窗口动画
if (params.windowAnimations == ) {
params.windowAnimations = a.getResourceId(
com.android.internal.R.styleable.Window_windowAnimationStyle, );
} // The rest are only done if this window is not embedded; otherwise,
// the values are inherited from our container.
if (getContainer() == null) {
if (mBackgroundDrawable == null) {
if (mBackgroundResource == ) {
mBackgroundResource = a.getResourceId(
com.android.internal.R.styleable.Window_windowBackground, );
}
if (mFrameResource == ) {
mFrameResource = a.getResourceId(com.android.internal.R.styleable.Window_windowFrame, );
}
if (false) {
System.out.println("Background: "
+ Integer.toHexString(mBackgroundResource) + " Frame: "
+ Integer.toHexString(mFrameResource));
}
}
mTextColor = a.getColor(com.android.internal.R.styleable.Window_textColor, 0xFF000000);
} // Inflate the window decor.
// 2、根据一些属性来选择不同的顶层视图布局,例如设置了FEATURE_NO_TITLE的属性,那么就选择没有Title区域的那么布局;
// layoutResource布局就是整个Activity的布局,其中含有title区域和content区域,content区域就是用来显示我通过
// setContentView设置进来的内容区域,也就是我们要显示的视图。 int layoutResource;
int features = getLocalFeatures();
// System.out.println("Features: 0x" + Integer.toHexString(features));
if ((features & (( << FEATURE_LEFT_ICON) | ( << FEATURE_RIGHT_ICON))) != ) {
if (mIsFloating) {
layoutResource = com.android.internal.R.layout.dialog_title_icons;
} else {
layoutResource = com.android.internal.R.layout.screen_title_icons;
}
// System.out.println("Title Icons!");
} else if ((features & (( << FEATURE_PROGRESS) | ( << FEATURE_INDETERMINATE_PROGRESS))) != ) {
// Special case for a window with only a progress bar (and title).
// XXX Need to have a no-title version of embedded windows.
layoutResource = com.android.internal.R.layout.screen_progress;
// System.out.println("Progress!");
} else if ((features & ( << FEATURE_CUSTOM_TITLE)) != ) {
// Special case for a window with a custom title.
// If the window is floating, we need a dialog layout
if (mIsFloating) {
layoutResource = com.android.internal.R.layout.dialog_custom_title;
} else {
layoutResource = com.android.internal.R.layout.screen_custom_title;
}
} else if ((features & ( << FEATURE_NO_TITLE)) == ) {
// If no other features and not embedded, only need a title.
// If the window is floating, we need a dialog layout
if (mIsFloating) {
layoutResource = com.android.internal.R.layout.dialog_title;
} else {
layoutResource = com.android.internal.R.layout.screen_title;
}
// System.out.println("Title!");
} else {
// Embedded, so no decoration is needed.
layoutResource = com.android.internal.R.layout.screen_simple;
// System.out.println("Simple!");
} mDecor.startChanging();
// 3、加载视图
View in = mLayoutInflater.inflate(layoutResource, null);
// 4、将layoutResource的内容添加到mDecor中
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
// 5、获取到我们的内容显示区域,这是一个ViewGroup类型的,其实是FrameLayout
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
} if ((features & ( << FEATURE_INDETERMINATE_PROGRESS)) != ) {
ProgressBar progress = getCircularProgressBar(false);
if (progress != null) {
progress.setIndeterminate(true);
}
} // 6、设置一些背景、title等属性
// Remaining setup -- of background and title -- that only applies
// to top-level windows.
if (getContainer() == null) {
Drawable drawable = mBackgroundDrawable;
if (mBackgroundResource != ) {
drawable = getContext().getResources().getDrawable(mBackgroundResource);
}
mDecor.setWindowBackground(drawable);
drawable = null;
if (mFrameResource != ) {
drawable = getContext().getResources().getDrawable(mFrameResource);
}
mDecor.setWindowFrame(drawable); // System.out.println("Text=" + Integer.toHexString(mTextColor) +
// " Sel=" + Integer.toHexString(mTextSelectedColor) +
// " Title=" + Integer.toHexString(mTitleColor)); if (mTitleColor == ) {
mTitleColor = mTextColor;
} if (mTitle != null) {
setTitle(mTitle);
}
setTitleColor(mTitleColor);
} mDecor.finishChanging(); return contentParent;
其实也就是这么几个步骤:
1、获取用户设置的一些属性与Flag;
2、根据一些属性选择不同的顶层视图布局,例如FEATURE_NO_TITLE则选择没有title的布局文件等;这里我们看一个与图1中符合的顶层布局吧,即layoutResource = c
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:fitsSystemWindows="true">
<!-- 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_width="match_parent"
android:layout_height="wrap_content" />
<!-- title区域-->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="?android:attr/windowTitleSize"
style="?android:attr/windowTitleBackgroundStyle">
<TextView android:id="@android:id/title"
style="?android:attr/windowTitleStyle"
android:background="@null"
android:fadingEdge="horizontal"
android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
<!--内容显示区域, 例如main_activity.xml布局就会被放到这个ViewGroup下面 -->
<FrameLayout android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight=""
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
我们可以看到有两个区域,即title区域和content区域,generateLayout函数中的
// 5、获取到我们的内容显示区域,这是一个ViewGroup类型的,其实是FrameLayout
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
获取的就是xml中id为content的FrameLayout,这个content就是我们的内容显示区域。整个布局对应的效果如下 :
这两个区域就组成了mDecor视图,我们的main_activity.xml就是放在内容视图这个区域的。
3、加载顶层布局文件,转换为View,将其添加到mDecor中;
4、获取内容容器Content Parent,即用于显示我们的内容的区域;
5、设置一些背景图和title等。
在经过这几步,我们就得到了mContentParent,这就是用来装载我们的视图的ViewGroup。再回过头来看setContentView函数:
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor(); // 1、生成DecorView,并且根据窗口属性加载顶级视图布局、获取mContentParent、设置一些基本属性等
} else {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent);// 2、将layoutResId加载到mContentParent中,这里的layoutResId就是我们的main_activity.xml
final Callback cb = getCallback();
if (cb != null) {
cb.onContentChanged();
}
}
我们看看LayoutInflater的inflate函数吧 :
/**
* Inflate a new view hierarchy from the specified xml resource. Throws
* {@link InflateException} if there is an error.
*
* @param resource ID for an XML layout resource to load (e.g.,
* <code>R.layout.main_page</code>)
* @param root Optional view to be the parent of the generated hierarchy.
* @return The root View of the inflated hierarchy. If root was supplied,
* this is the root View; otherwise it is the root of the inflated
* XML file.
*/
public View inflate(int resource, ViewGroup root) {
return inflate(resource, root, root != null);
} /**
* Inflate a new view hierarchy from the specified xml resource. Throws
* {@link InflateException} if there is an error.
*
* @param resource ID for an XML layout resource to load (e.g.,
* <code>R.layout.main_page</code>)
* @param root Optional view to be the parent of the generated hierarchy (if
* <em>attachToRoot</em> is true), or else simply an object that
* provides a set of LayoutParams values for root of the returned
* hierarchy (if <em>attachToRoot</em> is false.)
* @param attachToRoot Whether the inflated hierarchy should be attached to
* the root parameter? If false, root is only used to create the
* correct subclass of LayoutParams for the root view in the XML.
* @return The root View of the inflated hierarchy. If root was supplied and
* attachToRoot is true, this is root; otherwise it is the root of
* the inflated XML file.
*/
public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
if (DEBUG) System.out.println("INFLATING from resource: " + resource);
XmlResourceParser parser = getContext().getResources().getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
实际上就是将layoutResId这个布局的视图附加到mContentParent中。
DecorView
移步 : DecorView 。
ViewGroup
ViewGroup从语义上来说就是视图组,它也继承自View类,它其实就是视图的容器。我们看官方的定义 :
* A ViewGroup is a special view that can contain other views
* (called children.) The view group is the base class for layouts and views
* containers. This class also defines the
* {@link android.view.ViewGroup.LayoutParams} class which serves as the base
* class for layouts parameters.
我们通过ViewGroup来组织、管理子视图,例如我们常见的FrameLayout、LinearLayout、RelativeLayout、ListView等都是ViewGroup类型,总之只要能包含其他View或者ViewGroup的都是ViewGroup类型。使用ViewGroup来构建视图树。
View
View就是UI界面上的一个可见的组件,任何在UI上可见的都为View的子类。我们看官方定义 :
TextView、Button、ImageView、FrameLayout、LinearLayout、ListView等都是View的子类。
这样,ViewGroup类型的视图管理嵌套在里面的ViewGroup以及View控件组成了丰富多彩的用户界面。例如我们开篇的Hello World的视图结构是这样的 :
总结
整个窗口由Title区域和Content区域组成,Content区域就是我们要显示内容的区域,在这个区域中mContentParent是根ViewGroup,由mContentParent组织、管理其子视图,从而构建整个视图树。当Activity启动时,就将这些内容就会显示在手机上。
Activity的setContentView的流程的更多相关文章
- android源码解析(十七)-->Activity布局加载流程
版权声明:本文为博主原创文章,未经博主允许不得转载. 好吧,终于要开始讲讲Activity的布局加载流程了,大家都知道在Android体系中Activity扮演了一个界面展示的角色,这也是它与andr ...
- Android Activity之 setContentView()总结
从一开始hello world的第一个安卓应用开始,Activity 自动生成,布局自动生成,直接修改布局,在Activity中,findviewById()找到view,然后处理相应的业务逻辑即可, ...
- 《转》深入理解Activity启动流程(三)–Activity启动的详细流程2
本文原创作者:Cloud Chou. 出处:本文链接 本系列博客将详细阐述Activity的启动流程,这些博客基于Cm 10.1源码研究. 深入理解Activity启动流程(一)--Activity启 ...
- 《转》深入理解Activity启动流程(三)–Activity启动的详细流程1
本文原创作者:Cloud Chou. 出处:本文链接 本系列博客将详细阐述Activity的启动流程,这些博客基于Cm 10.1源码研究. 深入理解Activity启动流程(一)--Activity启 ...
- 《转》深入理解Activity启动流程(一)–Activity启动的概要流程
本文原创作者:Cloud Chou. 原文地址:http://www.cloudchou.com/android/post-788.html Android中启动某个Activity,将先启动Acti ...
- 深入理解Activity启动流程(三)–Activity启动的详细流程2
本文原创作者:Cloud Chou. 欢迎转载,请注明出处和本文链接 本系列博客将详细阐述Activity的启动流程,这些博客基于Cm 10.1源码研究. 深入理解Activity启动流程(一)--A ...
- 深入理解Activity启动流程(一)–Activity启动的概要流程
概述 Android中启动某个Activity,将先启动Activity所在的应用.应用启动时会启动一个以应用包名为进程名的进程,该进程有一个主线程,叫ActivityThread,也叫做UI线程. ...
- Activity的绘制流程简单分析(基于android 4.0源码进行分析)
要明白这个流程,我们还得从第一部开始,大家都知道 在activity里面 setcontentview 调用结束以后 就可以看到程序加载好我们的布局文件了,从而让我们在手机上看到这个画面. 那么我们来 ...
- 史上最全且最简洁易懂的Activity启动流程解析
Activity的启动流程是一个资深Android工程师必须掌握的内容,也是高职级面试中的高频面试知识点,无论是从事应用层开发,还是Framework开发,其重要性都无需我多言.而要真正理解它,就不可 ...
随机推荐
- [已解决]import pymssql ImportError: libsybdb.so.5
在python3.6的环境下安装pymssql安装包,运行程序时报错:import pymssql ImportError: libsybdb.so.5 在网上搜索了一大圈很多都是讲文件存在但是没有添 ...
- Spring+SpringMVC+MyBatis+easyUI整合优化篇
优化篇 Spring+SpringMVC+MyBatis+easyUI整合优化篇(一)System.out.print与Log Spring+SpringMVC+MyBatis+easyUI整合优化篇 ...
- Echarts---柱状图实现
做Echarts很简单,可以参看官网 http://echarts.baidu.com/index.html 作为程序员我们只需要把静态数据替换成我们自己需要的! 下面看一个自己做的例子: 还是先看看 ...
- HDU 1003 Max Sum【动态规划求最大子序列和详解 】
Max Sum Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Sub ...
- HDU 1013 Digital Roots【字符串,水】
Digital Roots Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Tot ...
- Codeforces Round #442 (Div. 2)
A. Alex and broken contest time limit per test 2 seconds memory limit per test 256 megabytes input s ...
- '<<' '|' '>>' 等位运算符 课本祥解
a<<1 相当于a*2 a>>1 相当于a/2 a<<|1 相当于a*2+1 一些算法用得到.
- 微信小程序个人心得
尊重原创:http://blog.csdn.net/qq_28832135/article/details/52796048 昨天看了一下微信小程序官方文档,总结一下自己学习的个人心得. 首先从官方文 ...
- NYOJ 2356 哈希计划(模拟)
题目链接: http://acm.nyist.me/JudgeOnline/problem.php?id=2356 题目描述 众所周知,LLM的算法之所以菜,就是因为成天打游戏,最近LLM突然想玩&l ...
- screen乱码问题
在 .screenrc下设置: defencoding gbk encoding gbk gbk detatch+reattach后,设置失效,这时可以直接用命令来配置: ctrl+a :defenc ...