本文主要讲解VIEw或者VIEwGroup是如何添加到应用程序的窗口中的。
1. 最简单的Activity一个Activity最简单的结构是如下:
public class MainActivity extends Activity { private button taskSwitch; @OverrIDe protected voID onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //那就从这个setContentVIEw开启我们的VIEw或者VIEwGroup加载之旅吧 setContentVIEw(R.layout.activity_main); } }
R.layout.activity_main.xml结构如下:
<relativeLayout xmlns:androID="http://schemas.androID.com/apk/res/androID" xmlns:tools="http://schemas.androID.com/tools" androID:layout_wIDth="match_parent" androID:layout_height="match_parent" androID:paddingBottom="@dimen/activity_vertical_margin" androID:paddingleft="@dimen/activity_horizontal_margin" androID:paddingRight="@dimen/activity_horizontal_margin" androID:paddingtop="@dimen/activity_vertical_margin" tools:context=".MainActivity" > <button androID:ID="@+ID/taskSwitch" androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" androID:text="@string/taskSwitch"/> </relativeLayout>
2. setContentVIEw关键流程顺序图Step1 Activity.setContentVIEw()这个方法是Activity里面的方法,直接回调了Window的getContentVIEw(),而mWindow对象实际上是PhoneWindow对象。
frameworks/base/core/java/androID/app/Activity.java
public class Activity extends ContextthemeWrapper implements LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2 { public voID setContentVIEw(int layoutResID) { getwindow().setContentVIEw(layoutResID); } public Window getwindow() { return mWindow; //Window对象,实际上是一个PhoneWindow对象,它是在AMS初始化的时候创建的 } }
AMS初始化的时候会调用PolicyManager.makeNewWindow(this);
public class Activity extends ContextthemeWrapper implements LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2 { 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) { attachBaseContext(context); mFragments.attachActivity(this, mContainer, null); mWindow = PolicyManager.makeNewWindow(this); //这里创建的实际上是PhoneWindow对象 mWindow.setCallback(this); mWindow.getLayoutInflater().setPrivateFactory(this); …… } }
而PolicyManager是调用的IPolicy的接口实现穿件Window的。
public final class PolicyManager { private static final String POliCY_IMPL_CLASS_name = "com.androID.internal.policy.impl.Policy"; private static final IPolicy sPolicy; …… public static Window makeNewWindow(Context context) { return sPolicy.makeNewWindow(context); } …… }
frameworks/base/policy/src/com/androID/internal/policy/impl/Policy.java实现了IPolicy接口,创建了Window对象,实际上是PhoneWindow对象
// Simple implementation of the policy interface that spawns the right // set of objects public class Policy implements IPolicy { private static final String TAG = "PhonePolicy"; …… public Window makeNewWindow(Context context) { return new PhoneWindow(context); //创建了Window对象,实际上是PhoneWindow对象 } }
到此,我们知道Activity.setContentVIEw()最终调用到的是PhoneWindow.setContentVIEw()。
Step2 PhoneWindow.setContentVIEw()如果第一次调用mDecor和mContentParent对象都为null,会调用installDecor(),如果不是,则调用mContentParent.removeAllVIEws()来删除所有的子VIEw,然后inflate所以的VIEw,再添加到mContentParent中。
public class PhoneWindow extends Window implements MenuBuilder.Callback { @OverrIDe public voID setContentVIEw(int layoutResID) { //如果第一次调用mDecor和mContentParent对象都为null,会调用installDecor(),如果不是,则调用mContentParent.removeAllVIEws() if (mContentParent == null) { installDecor(); } else { mContentParent.removeAllVIEws(); } mLayoutInflater.inflate(layoutResID, mContentParent); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } } }
Step2.1 PhoneWindow. installDecor() public class PhoneWindow extends Window implements MenuBuilder.Callback { private voID installDecor() { if (mDecor == null) { mDecor = generateDecor(); mDecor.setDescendantFocusability(VIEwGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootnamespace(true); if (!mInvalIDatePanelMenuPosted && mInvalIDatePanelMenuFeatures != 0) { mDecor.postOnAnimation(mInvalIDatePanelMenuRunnable); } } if (mContentParent == null) { 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); } } }); } } } } }
首先判断mDecor是否为空,如果是空,则调用generateDecor()创建一个新的DecorVIEw赋值给mDecor,该类是一个FrameLayout的子类,是一个VIEwGroup,当用HIErarchyVIEwer查看Layoutt的时候每个窗口最顶层都有的一个FrameLayout,这个FrameLayout就是这边的这个DecorVIEw。
protected DecorVIEw generateDecor() { return new DecorVIEw(getContext(), -1); } private final class DecorVIEw extends FrameLayout implements RootVIEwSurfaceTaker { }
然后判断mContentParent是否为空,如果为空,则调用generateLayout(mDecor)创建mContentParent。
protected VIEwGroup generateLayout(DecorVIEw decor) { // Apply data from current theme. // 1. 从com.androID.internal.R.styleable.Window也就是androID:theme=“”配置的值中获取Window的Style属性 // 2. 通过requestFeature方法设置Window.mFeatures和PhoneWindow.mLocalFeatures属性,设置宽口风格 TypedArray a = getwindowstyle(); if (false) { System.out.println("From style:"); String s = "Attrs:"; for (int i = 0; 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(0, flagsToUpdate); } else { setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate); } if (a.getBoolean(com.androID.internal.R.styleable.Window_windowNoTitle, false)) { requestFeature(FEATURE_NO_Title); } 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); } if (a.getBoolean(com.androID.internal.R.styleable.Window_windowActionModeOverlay, false)) { requestFeature(FEATURE_ACTION_MODE_OVERLAY); } 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_windowTranslucentStatus, false)) { setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS & (~getForceDWindowFlags())); } if (a.getBoolean(com.androID.internal.R.styleable.Window_windowTranslucentNavigation, false)) { setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION & (~getForceDWindowFlags())); } if (a.getBoolean(com.androID.internal.R.styleable.Window_windowOverscan, false)) { setFlags(FLAG_LAYOUT_IN_OVERSCAN, FLAG_LAYOUT_IN_OVERSCAN&(~getForceDWindowFlags())); } if (a.getBoolean(com.androID.internal.R.styleable.Window_windowshowWallpaper, false)) { setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForceDWindowFlags())); } if (a.getBoolean(com.androID.internal.R.styleable.Window_windowEnableSplittouch, getContext().getApplicationInfo().targetSdkVersion >= androID.os.Build.VERSION_CODES.HONEYCOMB)) { setFlags(FLAG_SPliT_touch, FLAG_SPliT_touch&(~getForceDWindowFlags())); } a.getValue(com.androID.internal.R.styleable.Window_windowMinWIDthMajor, mMinWIDthMajor); a.getValue(com.androID.internal.R.styleable.Window_windowMinWIDthMinor, mMinWIDthMinor); if (a.hasValue(com.androID.internal.R.styleable.Window_windowFixeDWIDthMajor)) { if (mFixeDWIDthMajor == null) mFixeDWIDthMajor = new TypedValue(); a.getValue(com.androID.internal.R.styleable.Window_windowFixeDWIDthMajor, mFixeDWIDthMajor); } if (a.hasValue(com.androID.internal.R.styleable.Window_windowFixeDWIDthMinor)) { if (mFixeDWIDthMinor == null) mFixeDWIDthMinor = new TypedValue(); a.getValue(com.androID.internal.R.styleable.Window_windowFixeDWIDthMinor, mFixeDWIDthMinor); } if (a.hasValue(com.androID.internal.R.styleable.Window_windowFixedHeightmajor)) { if (mFixedHeightmajor == null) mFixedHeightmajor = new TypedValue(); a.getValue(com.androID.internal.R.styleable.Window_windowFixedHeightmajor, mFixedHeightmajor); } if (a.hasValue(com.androID.internal.R.styleable.Window_windowFixedHeightminor)) { if (mFixedHeightminor == null) mFixedHeightminor = new TypedValue(); a.getValue(com.androID.internal.R.styleable.Window_windowFixedHeightminor, mFixedHeightminor); } // 3.读取SDK版本信息 final Context context = getContext(); final int targetSdk = context.getApplicationInfo().targetSdkVersion; final boolean targetPreHoneycomb = targetSdk < androID.os.Build.VERSION_CODES.HONEYCOMB; final boolean targetPreIcs = targetSdk < androID.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH; final boolean targetHcNeedsOptions = context.getResources().getBoolean( com.androID.internal.R.bool.target_honeycomb_needs_options_menu); final boolean noActionbar = !hasFeature(FEATURE_ACTION_bar) || hasFeature(FEATURE_NO_Title); if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionbar)) { addFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY); } else { clearFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY); } if (mAlwaysReadCloSEOntouchAttr || getContext().getApplicationInfo().targetSdkVersion >= androID.os.Build.VERSION_CODES.HONEYCOMB) { if (a.getBoolean( com.androID.internal.R.styleable.Window_windowCloSEOntouchOutsIDe, false)) { setCloSEOntouchOutsIDeIfNotSet(true); } } // 4. 获取LayoutParams信息用来判断是否输入法、是否模糊后面的,是否有窗口动画 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) == 0) { params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND; } if (!haveDimAmount()) { params.dimAmount = a.getfloat( androID.R.styleable.Window_backgroundDimAmount, 0.5f); } } if (params.windowAnimations == 0) { params.windowAnimations = a.getResourceID( com.androID.internal.R.styleable.Window_windowAnimationStyle, 0); } // 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 == 0) { mBackgroundResource = a.getResourceID( com.androID.internal.R.styleable.Window_windowBackground, 0); } if (mFrameResource == 0) { mFrameResource = a.getResourceID(com.androID.internal.R.styleable.Window_windowFrame, 0); } 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. // 5. 获取上面设置的PhoneWindow.mLocalFeatures,根据一些列的条件判断需要是哪个layoutResource,比如是否是全屏,是否有标题栏 // 有如下7种类型的layout: // com.androID.internal.R.layout.screen_Title_icons // com.androID.internal.R.layout.screen_progress // com.androID.internal.R.layout.screen_custom_Title // com.androID.internal.R.layout.screen_action_bar // com.androID.internal.R.layout.screen_Title // com.androID.internal.R.layout.screen_simple_overlay_action_mode // com.androID.internal.R.layout.screen_simple int layoutResource; int features = getLocalFeatures(); // System.out.println("Features: 0x" + Integer.toHexString(features)); 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; } // 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 = com.androID.internal.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( com.androID.internal.R.attr.dialogCustomTitleDecorLayout, res, true); layoutResource = res.resourceID; } else { layoutResource = com.androID.internal.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( com.androID.internal.R.attr.dialogTitleDecorLayout, res, true); layoutResource = res.resourceID; } else if ((features & (1 << FEATURE_ACTION_bar)) != 0) { layoutResource = com.androID.internal.R.layout.screen_action_bar; } else { layoutResource = com.androID.internal.R.layout.screen_Title; } // System.out.println("Title!"); } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) { layoutResource = com.androID.internal.R.layout.screen_simple_overlay_action_mode; } else { // Embedded, so no decoration is needed. layoutResource = com.androID.internal.R.layout.screen_simple; // System.out.println("Simple!"); } mDecor.startChanging(); // 6. 填充相应的layoutResource,并把它加到decor里面 VIEw in = mLayoutInflater.inflate(layoutResource, null); decor.addVIEw(in, new VIEwGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); // 7. 找出相应的ID_ANDROID_CONTENT的VIEwGroup,实际想就是上面layoutResource里面的<FrameLayout androID:ID="@androID:ID/content" /> 这个FrameLayout 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); } } // Remaining setup -- of background and Title -- that only applIEs // to top-level windows. if (getContainer() == null) { Drawable drawable = mBackgroundDrawable; if (mBackgroundResource != 0) { drawable = getContext().getResources().getDrawable(mBackgroundResource); } mDecor.setwindowBackground(drawable); drawable = null; if (mFrameResource != 0) { 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 == 0) { mTitlecolor = mTextcolor; } if (mTitle != null) { setTitle(mTitle); } setTitlecolor(mTitlecolor); } mDecor.finishChanging(); return contentParent; }
此时,我们拿一个常用的com.androID.internal.R.layout.screen_Title窗口布局文件来分析一下。
这个窗口布局文件分为3个部分:
<linearLayout xmlns:androID="http://schemas.androID.com/apk/res/androID" androID:fitsSystemwindows="true" androID:orIEntation="vertical" androID:layout_wIDth="match_parent" androID:layout_height="match_parent"> <!-- 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" /> <relativeLayout androID:ID="@androID:ID/Title_container" androID:layout_wIDth="match_parent" androID:layout_height="?androID:attr/windowTitleSize"> <!-- The Title background has 9px left padding. --> <ImageVIEw androID:ID="@androID:ID/left_icon" androID:visibility="gone" androID:layout_marginEnd="9dip" androID:layout_wIDth="16dip" androID:layout_height="16dip" androID:scaleType="fitCenter" androID:layout_alignParentStart="true" androID:layout_centerVertical="true" /> <Progressbar androID:ID="@+ID/progress_circular" androID:visibility="gone" androID:max="10000" androID:layout_centerVertical="true" androID:layout_alignParentEnd="true" androID:layout_marginStart="6dip" androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" /> <!-- There are 6dip between this and the circular progress on the right, we also make 6dip (with the -3dip margin_left) to the icon on the left or the screen left edge if no icon. This also places our left edge 3dip to the left of the Title text left edge. --> <Progressbar androID:ID="@+ID/progress_horizontal" androID:layout_wIDth="match_parent" androID:layout_height="wrap_content" androID:layout_marginStart="-3dip" androID:layout_toStartOf="@androID:ID/progress_circular" androID:layout_toEndOf="@androID:ID/left_icon" androID:layout_centerVertical="true" androID:visibility="gone" androID:max="10000" /> <linearLayout androID:layout_wIDth="match_parent" androID:layout_height="match_parent" androID:orIEntation="horizontal" androID:layout_toStartOf="@ID/progress_circular" androID:layout_toEndOf="@androID:ID/left_icon" > <TextVIEw androID:ID="@androID:ID/Title" androID:layout_wIDth="0dip" androID:layout_height="match_parent" androID:layout_weight="1" androID:background="@null" androID:fadingEdge="horizontal" androID:scrollHorizontally="true" androID:gravity="center_vertical" androID:layout_marginEnd="2dip" /> <!-- 2dip between the icon and the Title text, if icon is present. --> <ImageVIEw androID:ID="@androID:ID/right_icon" androID:visibility="gone" androID:layout_wIDth="16dip" androID:layout_height="16dip" androID:layout_weight="0" androID:layout_gravity="center_vertical" androID:scaleType="fitCenter" /> </linearLayout> </relativeLayout> <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" /> </linearLayout>
Step2.2 LayoutInflater.inflate()前一步结束之后,我们需要的mDecor和mContentParent都已经初始化完毕,mContentParent( )就是我们Activity中setContentVIEw的视图所要填充到的父视图。
public VIEw inflate(int resource, VIEwGroup root) { return inflate(resource, root, root != null); } public VIEw inflate(XmlPullParser parser, VIEwGroup root) { return inflate(parser, root, root != null); } 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(); } } public vIEw inflate(xmlpullparser parser, vIEwgroup root, boolean attachtoroot) { synchronized (mconstructorargs) { trace.tracebegin(trace.trace_tag_vIEw, "inflate"); final attributeset attrs = xml.asattributeset(parser); context lastcontext = (context)mconstructorargs[0]; mconstructorargs[0] = mcontext; vIEw result = root; try { // 1. 查找并初始化跟试图节点 // look for the root node. …… final string name = parser.getname(); if (tag_merge.equals(name)) { if (root == null || !attachtoroot) { throw new inflateexception("<merge /> can be used only with a valID " + "vIEwgroup root and attachtoroot=true"); } rinflate(parser, root, attrs, false); } else { // temp is the root vIEw that was found in the xml vIEw temp; if (tag_1995.equals(name)) { temp = new blinklayout(mcontext, attrs); } else { temp = createvIEwfromtag(root, name, attrs); } vIEwgroup.layoutparams params = null; if (root != null) { if (deBUG) { system.out.println("creating params from root: " + root); } // create layout params that match root, if supplIEd params = root.generatelayoutparams(attrs); if (!attachtoroot) { // set the layout params for temp if we are not // attaching. (if we are, we use addvIEw, below) temp.setlayoutparams(params); } } if (deBUG) { system.out.println("-----> start inflating children"); } // 创建所有的子试图节点 // inflate all children under temp rinflate(parser, temp, attrs, true); …… } } catch (xmlpullparserexception e) { inflateexception ex = new inflateexception(e.getmessage()); ex.initcause(e); throw ex; } catch (ioexception e) { inflateexception ex = new inflateexception( parser.getpositiondescription() + ": " + e.getmessage()); ex.initcause(e); throw ex; } finally { // don't retain static reference on context. mconstructorargs[0] = lastcontext; mconstructorargs[1] = null; } trace.traceend(trace.trace_tag_vIEw); return result; } }
实际上mLayoutInflater实际上是PhoneLayoutInflater的对象,可以分析Step1中PhoneWindow的初始化过程得知。可以看如下代码:
public class PhoneWindow extends Window implements MenuBuilder.Callback { public PhoneWindow(Context context) { super(context); mLayoutInflater = LayoutInflater.from(context); } } public abstract class LayoutInflater { public static LayoutInflater from(Context context) { LayoutInflater LayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); if (LayoutInflater == null) { throw new AssertionError("LayoutInflater not found."); } return LayoutInflater; } } class ContextImpl extends Context { static { registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { return PolicyManager.makeNewLayoutInflater(ctx.getouterContext()); }}); } private static final HashMap<String, ServiceFetcher> SYstem_SERVICE_MAP = new HashMap<String, ServiceFetcher>(); private static int sNextPerContextServiceCacheIndex = 0; private static voID registerService(String servicename, ServiceFetcher fetcher) { if (!(fetcher instanceof StaticServiceFetcher)) { fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++; } SYstem_SERVICE_MAP.put(servicename, fetcher); } public Object getSystemService(String name) { ServiceFetcher fetcher = SYstem_SERVICE_MAP.get(name); return fetcher == null ? null : fetcher.getService(this); } } public final class PolicyManager { private static final String POliCY_IMPL_CLASS_name = "com.androID.internal.policy.impl.Policy"; private static final IPolicy sPolicy; static { // Pull in the actual implementation of the policy at run-time try { Class policyClass = Class.forname(POliCY_IMPL_CLASS_name); sPolicy = (IPolicy)policyClass.newInstance(); } …… } public static LayoutInflater makeNewLayoutInflater(Context context) { return sPolicy.makeNewLayoutInflater(context); } } public class Policy implements IPolicy { public LayoutInflater makeNewLayoutInflater(Context context) { return new PhoneLayoutInflater(context); } }
至此,总的流程都已经结束了,我们的Acitivity.setContentVIEw就差不多分析完毕了。总的来说上面的过程可以概括为3步:1、创建一个DecorVIEw对象,该对象将作为整个应用窗口的根视图
2、创建不同的窗口布局文件,并且获取Activity的布局文件该存放的地方,即该窗口布局文件内ID为content的FrameLayout指定() 。
3、将Activity的布局文件添加至ID为content的FrameLayout内。
----
从上面我们可以得知,每一个VIEw或者VIEwGroup都是通过调用LayoutInflater.createvIEwfromtag()来进行VIEw的穿件
VIEw createVIEwFromTag(VIEw parent, String name, AttributeSet attrs) { if (name.equals("vIEw")) { name = attrs.getAttributeValue(null, "class"); } if (DEBUG) System.out.println("******** Creating vIEw: " + name); try { VIEw vIEw; // 此处几个Factory都不会执行 if (mFactory2 != null) vIEw = mFactory2.onCreateVIEw(parent, name, mContext, attrs); else if (mFactory != null) vIEw = mFactory.onCreateVIEw(name, mContext, attrs); else vIEw = null; if (vIEw == null && mPrivateFactory != null) { vIEw = mPrivateFactory.onCreateVIEw(parent, name, mContext, attrs); } if (vIEw == null) { // 根据VIEw是否是内置判断需要调用的方法: // 如果是内置的(androID.Widget或androID.webkit或andriod.vIEw包里的)androID自身类就调用LayoutInflater.onCreateVIEw (3parameters),如果不是就调用LayoutInflater.createVIEw 如果不是内置(自定义的,或者目录不在上述几个包中的)则调用LayoutInflater.createVIEw(name, null, attrs); if (-1 == name.indexOf('.')) { vIEw = onCreateVIEw(parent, name, attrs); } else { vIEw = createVIEw(name, null, attrs); } } if (DEBUG) System.out.println("Created vIEw is: " + vIEw); return vIEw; …… }
根据VIEw是否是内置判断需要调用的方法:
如果是内置的(androID.Widget或androID.webkit或andriod.vIEw包里的)androID自身类就调用LayoutInflater.onCreateVIEw (3parameters),如果不是就调用LayoutInflater.createVIEw;
如果不是内置(自定义的,或者目录不在上述几个包中的)则调用LayoutInflater.createVIEw(name, null, attrs);
如果是内置的(androID.Widget或androID.webkit)androID自身类就调用LayoutInflater.createVIEw,如果不是就调用LayoutInflater.onCreateVIEw(2 parameters);
public abstract class LayoutInflater { protected VIEw onCreateVIEw(String name, AttributeSet attrs) throws ClassNotFoundException { return createVIEw(name, "androID.vIEw.", attrs); } protected VIEw onCreateVIEw(VIEw parent, String name, AttributeSet attrs) throws ClassNotFoundException { // 1. 根据多态性,直接调用PhoneLayoutInflater.onCreateVIEw(2 parameters) return onCreateVIEw(name, attrs); } } public class PhoneLayoutInflater extends LayoutInflater { private static final String[] sClassprefixList = { "androID.Widget.", "androID.webkit." }; @OverrIDe protected VIEw onCreateVIEw(String name, AttributeSet attrs) throws ClassNotFoundException { // 2. 如果是内置的(androID.Widget或androID.webkit)androID自身类就调用LayoutInflater.createVIEw,如果不是就调用LayoutInflater.onCreateVIEw(2 parameters) for (String prefix : sClassprefixList) { try { VIEw vIEw = createVIEw(name, prefix, attrs); if (vIEw != null) { return vIEw; } } catch (ClassNotFoundException e) { // In this case we want to let the base class take a crack // at it. } } // 2. return super.onCreateVIEw(name, attrs); } } 不过最终都是要调用createVIEw()方法来进行VIEw或者VIEwGroup的创建。createVIEw()的方法气势很简答,就是通过反射机制把VIEw或者VIEwGroup给创建出来。 public final VIEw createVIEw(String name, String prefix, AttributeSet attrs) throws ClassNotFoundException, InflateException { // 1. 如果sConstructorMap里面已经有对应的类,则直接取出来, // 如果没有,则通过发射机制把对应的类给loadClass进来; Constructor<? extends VIEw> constructor = sConstructorMap.get(name); Class<? extends VIEw> clazz = null; try { Trace.traceBegin(Trace.TRACE_TAG_VIEW, name); if (constructor == null) { // Class not found in the cache, see if it's real, and try to add it clazz = mContext.getClassLoader().loadClass( prefix != null ? (prefix + name) : name).asSubclass(VIEw.class); if (mFilter != null && clazz != null) { boolean allowed = mFilter.onLoadClass(clazz); if (!allowed) { failNotAllowed(name, prefix, attrs); } } constructor = clazz.getConstructor(mConstructorSignature); sConstructorMap.put(name, constructor); } else { // If we have a filter, apply it to cached constructor if (mFilter != null) { // Have we seen this name before? Boolean allowedState = mFilterMap.get(name); if (allowedState == null) { // New class -- remember whether it is allowed clazz = mContext.getClassLoader().loadClass( prefix != null ? (prefix + name) : name).asSubclass(VIEw.class); boolean allowed = clazz != null && mFilter.onLoadClass(clazz); mFilterMap.put(name, allowed); if (!allowed) { failNotAllowed(name, prefix, attrs); } } else if (allowedState.equals(Boolean.FALSE)) { failNotAllowed(name, prefix, attrs); } } } Object[] args = mConstructorArgs; args[1] = attrs; // 2. 创建对应的VIEw或者VIEwGroup对象。 constructor.setAccessible(true); final VIEw vIEw = constructor.newInstance(args); if (vIEw instanceof VIEwStub) { // always use ourselves when inflating VIEwStub later final VIEwStub vIEwStub = (VIEwStub) vIEw; vIEwStub.setLayoutInflater(this); } return vIEw; } …… }
转载于:https://www.cnblogs.com/GMCisMarkdownCraftsman/p/3754407.HTML
总结以上是内存溢出为你收集整理的从setContentView()谈起全部内容,希望文章能够帮你解决从setContentView()谈起所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)