Android setContentView()解读
来源:互联网 发布:淘宝信用贷款怎么审查 编辑:程序博客网 时间:2024/06/06 01:56
本篇分享的目的是了解Activity的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(int layoutResID) {
getWindow().setContentView(layoutResID);
initActionBar();
}
直接翻译就是:
设置Activity内容布局的现实,这个布局来自项目的资源(res)文件夹、这个布局文件将会被初始化成一个View,被添加到Activity最顶层的View中。
查看Activity级别的源代码,如上的源代码可知,具体设置layoutResID的操作,是在getWindow()方法返回的对象执行,查看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.
*/
public Window getWindow() {
return mWindow;
}
mWindow 是 Window类的实例,Activity源代码是这样定义的:
private Window mWindow;
那么Window类的功能都有哪些呢,顾名思义Window是窗口的意思,也就说Activity所有直面用户界面其实是Window类的对象,设置包含按键处理,这由Window类定义的注释可知
/**
* Abstract base class for a top-level window look and behavior policy. An
* instance of this class should be used as the top-level view added to the
* window manager. It provides standard UI policies such as a background, title
* area, default key processing, etc.
*
* <p>The only existing implementation of this abstract class is
* android.policy.PhoneWindow, which you should instantiate when needing a
* Window. Eventually that class will be refactored and a factory method
* added for creating Window instances without knowing about a particular
* implementation.
*/
public abstract class Window {
}
由定义可知 Window类是抽象类型,不能直接去创建实例,很多抽象方法需要实现的,那么我们查看Activity中是如何去实例化mWindow对象的?
最简单直接的方法,Ctrl+F 查找Activity源代码中,只有一个地方是实例化mWindow对象的,如下源代码:
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config) {
……
mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);
……
}
由此源代码我们可以知道,它是由PolicyManager类makeNewWindow(this)方法去实例化的,下面目的就很明确了,去查看PolicyManager源代码就可以了。
PolicyManager: com.android.internal.policy.PolicyManager 此类是来自于 com.android.internal.policy 包下面的。
很遗憾的是我电脑上的,android.jar 4.4 源代码没有此类的代码,给大家推荐一个查看源代码的网址:http://www.grepcode.com/
直接在搜索框输入:com.android.internal.policy.PolicyManager 进行搜索,选择源码4.4 版本的,就可以得到此代源代码,
查看PolicyManager.makeNewWindow(this)如下:
// The static methods to spawn new policy-specific objects
public static Window makeNewWindow(Context context) {
return sPolicysPolicy.makeNewWindow(context);
}
由此源代码可知,sPolicy是真正创造Window类对象的实例,那么顺藤摸瓜继续追踪,可知sPolicy 定义如下:
private static final IPolicy sPolicy;
而IPolicy 是一个接口,不可能是具体的实现的,那么就查看sPolicy 是如何在PolicyManager被初始化的,sPolicy是在PolicyManager初始代码块被初始化的,代码如下:
static {
// Pull in the actual implementation of the policy at run-time
try {
Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
sPolicysPolicy = (IPolicy)policyClass.newInstance();
} catch (ClassNotFoundException ex) {
……
}
由如上的源代码可知sPolicy对象,是由反射的类创建的实例,而反射的路径就是 常量:POLICY_IMPL_CLASS_NAME=“com.android.internal.policy.impl.Policy”;
思考: 这里为什么要用反射的方式来做?
那么我们就查看 “com.android.internal.policy.impl.Policy” 类的源代码,还是从http://www.grepcode.com/上查询。
我们很容易就找到了Policy类makeNewWindow方法,实现如下:
public Window makeNewWindow(Context context) {
return new PhoneWindow(context);
}
至此我们知道了,Activity中呈现视图显示的对象其实是:PhoneWindow(Window的子类)类的实例
好了,同样的操作我们进入 PhoneWindow 源代码,查看它的setContentView方法是如何实现,
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
//添加程序员自定义Layout到mContentParent中
mLayoutInflater.inflate(layoutResID, mContentParent);
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
由mContentParent定义可知它其实是一个ViewGroup对象,由其变量命名的意义可以知道,它应该是Activity具体内容布局View的父容器。
如果此时是空的,就执行installDecor(),方法实现如下:
private void installDecor() {
//mDecor是DecorView类,而DecorView是FrameLayout子类,这里可以理解为FrameLayout
if (mDecor == null) {
//generateDecor()方法就是为了创建一个DecorView对象
mDecor = generateDecor();
//设置其与本身与子View可获得焦点的顺序
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
//是否属于根空间
mDecor.setIsRootNamespace(true);
//菜单的相关设置
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
if (mContentParent == null) {
//此方法配置了Window样式、为mDecor添加容器框架、返回自定义内容的布局View
mContentParent = generateLayout(mDecor);
// Set up decor part of UI to ignore fitsSystemWindows if appropriate.
mDecor.makeOptionalFitsSystemWindows();
mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
if (mTitleView != null) {
mTitleView.setLayoutDirection(mDecor.getLayoutDirection());
if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
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);
}
} else {
mActionBar = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
if (mActionBar != null) {
mActionBar.setWindowCallback(getCallback());
if (mActionBar.getTitle() == null) {
mActionBar.setWindowTitle(mTitle);
}
final int localFeatures = getLocalFeatures();
if ((localFeatures & (1 << FEATURE_PROGRESS)) != 0) {
mActionBar.initProgress();
}
if ((localFeatures & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
mActionBar.initIndeterminateProgress();
}
final ActionBarOverlayLayout abol = (ActionBarOverlayLayout) findViewById(
com.android.internal.R.id.action_bar_overlay_layout);
if (abol != null) {
abol.setOverlayMode(
(localFeatures & (1 << FEATURE_ACTION_BAR_OVERLAY)) != 0);
}
boolean splitActionBar = false;
final boolean splitWhenNarrow =
(mUiOptions &ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW) != 0;
if (splitWhenNarrow) {
splitActionBar = getContext().getResources().getBoolean(
com.android.internal.R.bool.split_action_bar_is_narrow);
} else {
splitActionBar = getWindowStyle().getBoolean(
com.android.internal.R.styleable.Window_windowSplitActionBar,false);
}
final ActionBarContainer splitView = (ActionBarContainer) findViewById(
com.android.internal.R.id.split_action_bar);
if (splitView != null) {
mActionBar.setSplitView(splitView);
mActionBar.setSplitActionBar(splitActionBar);
mActionBar.setSplitWhenNarrow(splitWhenNarrow);
final ActionBarContextView cab = (ActionBarContextView) findViewById(
com.android.internal.R.id.action_context_bar);
cab.setSplitView(splitView);
cab.setSplitActionBar(splitActionBar);
cab.setSplitWhenNarrow(splitWhenNarrow);
} else if (splitActionBar) {
Log.e(TAG, "Requested split action bar with " +
"incompatible window decor! Ignoring request.");
}
if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 ||
(mIconRes != 0 && !mActionBar.hasIcon())) {
mActionBar.setIcon(mIconRes);
} else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 &&
mIconRes == 0 && !mActionBar.hasIcon()) {
mActionBar.setIcon(
getContext().getPackageManager().getDefaultActivityIcon());
mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK;
}
if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0 ||
(mLogoRes != 0 && !mActionBar.hasLogo())) {
mActionBar.setLogo(mLogoRes);
}
// Post the panel invalidate for later; avoid application onCreateOptionsMenu
// being called in the middle of onCreate or similar.
mDecor.post(new Runnable() {
public void run() {
// Invalidate if the panel menu hasn't been created before this.
PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL,false);
if (!isDestroyed() && (st == null || st.menu == null)) {
invalidatePanelMenu(FEATURE_ACTION_BAR);
}
}
});
}
}
}
}
重点方法-generateLayout()
- Android setContentView()解读
- android setContentView()
- android setContentView
- android setContentView
- Android 页面间的转换(setContentView)
- Android setContentView方法解析(一)
- Android setContentView方法解析(二)
- Android自定义view浅谈(一)setContentView
- Android setContentView的位置
- Android setContentView、LayoutInflater、findViewById
- Android中的setContentView用法
- Android OnCreate、setContentView方法
- Android setContentView源码解析
- Android setContentView源码解析
- Android中的setContentView( )方法
- Android源码解析setContentView
- Android setContentView()源码解析
- android setContentView()原理
- 进程间通信的几种方式:管道、信号、消息队列、共享内存
- SPSS聚类分析——一个案例演示聚类分析全过程
- mongoDB的读书笔记(via3.0)(00)_【概览】(01)_mongoDB3.0的一些变化
- 1、MySQL-服务打开
- java中文乱码解决之道(三)—–编码详情:伟大的创想—Unicode编码
- Android setContentView()解读
- Hadoop平台搭建使用系列教程(7)- SSH无密码验证
- 序列化:Serializable、Externalizable、Parcelable
- Eclipse快捷键
- CentOS 的用户、组权限、添加删除用户等操作的详细操作命令
- 使用ReactiveCocoa实现iOS平台响应式编程
- MySQL error log 输出到syslog
- 2、MySQL-登录与退出
- HDOJ-3974(线段树,区间修改点查询)