聊聊PhoneWindow,getDecorView(),setContentView
2017-11-03 22:47
323 查看
转载请注明出处
csdn:http://blog.csdn.net/dreamsever/article/details/78440417
简书:http://www.jianshu.com/p/a4618a9290e1
这里大部分参考自Android窗口机制(二)Window,PhoneWindow,DecorView,setContentView源码理解
这个抽象类的唯一现有的实现是android.view.PhoneWindow,当你需要一个Window的时候你应该实例化它。
以上是官方描述,我的理解Window就是一个窗口,最直观的表现就是一个界面的载体。
延伸一下:
那么Activity跟Window又是什么关系呢?Activity是我们开发app中打交道最多的一个类,它是一个用户交互界面。那么它怎么是一个用户交互界面呢?你不能说它是它就是,Activity的用户交互体现在setContentView(@LayoutRes int layoutResID)方法,布局xml文件就是界面展现,有布局肯定就是有界面了。使用Activity的setContentView(@LayoutRes int layoutResID)将布局文件与Activity绑定,那么它是怎么绑定的,绑定到哪去了?这里就要用到Window了,Window是一个窗口,它有一个DecorView,而DecorView就是具体承载布局文件的view,后面具体分析
这个方法是获取到顶层窗口的装饰视图(包含标准窗口框架/装饰以及其内部的客户端内容),可以将其作为窗口添加到窗口管理器。
那么什么是装饰视图DecorView呢,
在PhoneWindow里面,出现了成员变量DecorView,它是一个window的顶层视图,DecorView继承于FrameLayout,我们那些标题栏,内容栏,顶级上看是加载在DecorView上的。而DecorView则是由PhoneWindow负责添加。
下面是Activity的setContentView
指向的是Window的setContentView
也就是PhoneWindow的具体实现
我们看到当mContentParent为null的时候会执行installDecor()第一次进来mContentParent肯定为null
通过看installDecor的源码我们发现,这个方法中做了两件事:
生成DecorView
mDecor = generateDecor(-1);
和生成布局
mContentParent = generateLayout(mDecor);
generateDecor这个方法我们不用多看,也没什么东西,我们只需要知道生成一个DecorView的实例名字叫mDecor就行了
generateLayout这个方法里面的东西太多,我前面删除一部分
这里layoutResource,是根据我们设置的状态栏主题,判断DecorView自动为我们加载什么样的布局。当我们将主题设置为NoTitleBar时,generateLayout方法中的layoutResource变量值为R.layout.screen_simple,所以我们看下系统这个screen_simple.xml布局文件
当拿到这个layoutResource之后,执行的是mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
这里我们看到执行了一下mDecorCaptionView = createDecorCaptionView(inflater);
上面的代码我也没看懂,但是看结果是mDecorCaptionView的创建结果还是空,当mDecorCaptionView == null的时候会把我们加载出来的final View root = inflater.inflate(layoutResource, null);添加到DecorView的第0个位置,也就是一个FrameLayout视图最下面,就是是把刚才那个R.layout.screen_simple文件添加到DecorView最下面,也许看下面的Hierarchy View视图更直观一些,而且我们还看到了DecorView里面有一个LinearLayout并且index为0,细心的会发现DecorView还包含了NavigationBar View 还有StatusBar View,这些东西是什么时候加进去的呢?这个先不管,也可以自己去看DecorView的源码刨根问底。
现在我们在拐回到刚才的generateLayout方法
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
其实就是执行的getDecorView().findViewById,这个东西是谁呢?就是前面加载的R.layout.screen_simple文件里面的FrameLayout android:id=”@android:id/content”。最后contentParent作为generateLayout方法的返回赋值给mContentParent = generateLayout(mDecor);
写了一大圈我们再回到PhoneWindow的setContentView(int layoutResID)方法,执行mLayoutInflater.inflate(layoutResID, mContentParent);将我们Activity的R.layout.activity_main布局加载到mContentParent里面去,再看下面的图也许会更清晰一些。
1,activity.findViewById(Window.ID_ANDROID_CONTENT)拿到的是谁?
答:本文中拿到的是R.layout.sample布局文件的FrameLayout,其实根据不同的主题拿到不同的布局文件中的FrameLayout,它用来存放我们setContentView(id)时加载的布局
2,activity.getWindow().getDecorView().getChildAt(0)拿到的是谁?
答:本文中拿到的是R.layout.sample布局文件,其实根据不同的主题设置拿到不同的布局文件
3,statusBar在哪里?
答:在DecorView里面,可以去看上面的Hierarchy View视图,也可以自己使用Hierarchy View看自己布局的视图更直观些。
http://blog.csdn.net/yanbober/article/details/45970721
http://blog.csdn.net/mr_liabill/article/details/49534851
http://www.jianshu.com/p/983eb8d5bb1a
http://www.jianshu.com/p/afa921d8ed24
http://blog.csdn.net/hohohong/article/details/54412464
csdn:http://blog.csdn.net/dreamsever/article/details/78440417
简书:http://www.jianshu.com/p/a4618a9290e1
前言
最近想实现一下滑动返回,看了一下几个开源的滑动返回的库,或多或少都有点问题,就想自己做一下。看了一下他们的源码,发现直接被activity.getWindow().getDecorView(),activity.getWindow().getDecorView().getChildAt(0),activity.findViewById(Window.ID_ANDROID_CONTENT)整蒙了,它们具体代表一个界面的哪些区域。作为一个三年的Android开发我竟然说不清楚,好惭愧。今天就来总结一下PhoneWindow,activity.getWindow().getDecorView(),statusBar,activity.findViewById(Window.ID_ANDROID_CONTENT)等等它们的边边角角,前生今生。分析
先谈谈window,PhoneWindow,getDecorView()这里大部分参考自Android窗口机制(二)Window,PhoneWindow,DecorView,setContentView源码理解
Window
它是一个抽象基类,代表顶级窗口的外观及行为策略,这个类的一个实例应该被用作添加到窗口管理器的顶层视图。它提供了标准的UI策略,如背景,标题区域,默认密钥处理等。这个抽象类的唯一现有的实现是android.view.PhoneWindow,当你需要一个Window的时候你应该实例化它。
以上是官方描述,我的理解Window就是一个窗口,最直观的表现就是一个界面的载体。
延伸一下:
那么Activity跟Window又是什么关系呢?Activity是我们开发app中打交道最多的一个类,它是一个用户交互界面。那么它怎么是一个用户交互界面呢?你不能说它是它就是,Activity的用户交互体现在setContentView(@LayoutRes int layoutResID)方法,布局xml文件就是界面展现,有布局肯定就是有界面了。使用Activity的setContentView(@LayoutRes int layoutResID)将布局文件与Activity绑定,那么它是怎么绑定的,绑定到哪去了?这里就要用到Window了,Window是一个窗口,它有一个DecorView,而DecorView就是具体承载布局文件的view,后面具体分析
PhoneWindow
它是Window的唯一实现类,也就是说Window就是一个抽象,想要具体实现,具体操作还是要靠这个PhoneWindowgetDecorView()
这个方法在Window的源码中/** * Retrieve the top-level window decor view (containing the standard * window frame/decorations and the client's content inside of that), which * can be added as a window to the window manager. * * <p><em>Note that calling this function for the first time "locks in" * various window characteristics as described in * {@link #setContentView(View, android.view.ViewGroup.LayoutParams)}.</em></p> * * @return Returns the top-level window decor view. */ public abstract View getDecorView();
这个方法是获取到顶层窗口的装饰视图(包含标准窗口框架/装饰以及其内部的客户端内容),可以将其作为窗口添加到窗口管理器。
那么什么是装饰视图DecorView呢,
/** @hide */ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks { ... DecorView(Context context, int featureId, PhoneWindow window, WindowManager.LayoutParams params) { super(context); mFeatureId = featureId; mShowInterpolator = AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in); mHideInterpolator = AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_linear_in); mBarEnterExitDuration = context.getResources().getInteger( R.integer.dock_enter_exit_duration); mForceWindowDrawsStatusBarBackground = context.getResources().getBoolean( R.bool.config_forceWindowDrawsStatusBarBackground) && context.getApplicationInfo().targetSdkVersion >= N; mSemiTransparentStatusBarColor = context.getResources().getColor( R.color.system_bar_background_semi_transparent, null /* theme */); updateAvailableWidth(); setWindow(window); updateLogTag(params); mResizeShadowSize = context.getResources().getDimensionPixelSize( R.dimen.resize_shadow_size); initResizingPaints(); } ... }
在PhoneWindow里面,出现了成员变量DecorView,它是一个window的顶层视图,DecorView继承于FrameLayout,我们那些标题栏,内容栏,顶级上看是加载在DecorView上的。而DecorView则是由PhoneWindow负责添加。
setContentView
上面我们有个疑问,Activity的setContentView(@LayoutRes int layoutResID)将布局文件与Activity绑定,那么它是怎么绑定的,绑定到哪去了?这里我们就好好分析一下Activity的setContentView,说Activity是一个界面,那只是抽象的描述,具体还是要体现在布局和view上。一个Activity包含一个Window,这个Window的实例化就是PhoneWindow,PhoneWindow中又有一个DecorView,DecorView继承自FrameLayout,好了现在布局有了,往里面添加View那不就是布局和View都有了,那不就是可以组成一个完整的界面了。请看源码娓娓道来下面是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(); }
指向的是Window的setContentView
/** * 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) */ public abstract void setContentView(@LayoutRes int layoutResID);
也就是PhoneWindow的具体实现
@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; }
我们看到当mContentParent为null的时候会执行installDecor()第一次进来mContentParent肯定为null
private void installDecor() { mForceDecorInstall = false; if (mDecor == null) { 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(); final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById( R.id.decor_content_parent); if (decorContentParent != null) { mDecorContentParent = decorContentParent; mDecorContentParent.setWindowCallback(getCallback()); if (mDecorContentParent.getTitle() == null) { mDecorContentParent.setWindowTitle(mTitle); } final int localFeatures = getLocalFeatures(); for (int i = 0; i < FEATURE_MAX; i++) { if ((localFeatures & (1 << i)) != 0) { mDecorContentParent.initFeature(i); } } mDecorContentParent.setUiOptions(mUiOptions); if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 || (mIconRes != 0 && !mDecorContentParent.hasIcon())) { mDecorContentParent.setIcon(mIconRes); } else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 && mIconRes == 0 && !mDecorContentParent.hasIcon()) { mDecorContentParent.setIcon( getContext().getPackageManager().getDefaultActivityIcon()); mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK; } if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0 || (mLogoRes != 0 && !mDecorContentParent.hasLogo())) { mDecorContentParent.setLogo(mLogoRes); } // Invalidate if the panel menu hasn't been created before this. // Panel menu invalidation is deferred avoiding application onCreateOptionsMenu // being called in the middle of onCreate or similar. // A pending invalidation will typically be resolved before the posted message // would run normally in order to satisfy instance state restoration. PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false); if (!isDestroyed() && (st == null || st.menu == null) && !mIsStartingWindow) { invalidatePanelMenu(FEATURE_ACTION_BAR); } } else { mTitleView = (TextView) findViewById(R.id.title); if (mTitleView != null) { if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) { final View titleContainer = findViewById(R.id.title_container); if (titleContainer != null) { titleContainer.setVisibility(View.GONE); } else { mTitleView.setVisibility(View.GONE); } mContentParent.setForeground(null); } else { mTitleView.setText(mTitle); } } } ... } }
通过看installDecor的源码我们发现,这个方法中做了两件事:
生成DecorView
mDecor = generateDecor(-1);
和生成布局
mContentParent = generateLayout(mDecor);
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().getResources()); if (mTheme != -1) { context.setTheme(mTheme); } } } else { context = getContext(); } return new DecorView(context, featureId, this, getAttributes()); }
generateDecor这个方法我们不用多看,也没什么东西,我们只需要知道生成一个DecorView的实例名字叫mDecor就行了
protected ViewGroup generateLayout(DecorView decor) { 。。。 // Inflate the window decor. 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; } 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) { // 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( R.attr.dialogCustomTitleDecorLayout, res, true); layoutResource = res.resourceId; } else { layoutResource = 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( R.attr.dialogTitleDecorLayout, res, true); layoutResource = res.resourceId; } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) { layoutResource = a.getResourceId( R.styleable.Window_windowActionBarFullscreenDecorLayout, R.layout.screen_action_bar); } else { layoutResource = R.layout.screen_title; } // System.out.println("Title!"); } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) { layoutResource = R.layout.screen_simple_overlay_action_mode; } else { // Embedded, so no decoration is needed. layoutResource = R.layout.screen_simple; // System.out.println("Simple!"); } mDecor.startChanging(); mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); 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); } } if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) { registerSwipeCallbacks(); } // Remaining setup -- of background and title -- that only applies // to top-level windows. if (getContainer() == null) { final Drawable background; if (mBackgroundResource != 0) { background = getContext().getDrawable(mBackgroundResource); } else { background = mBackgroundDrawable; } mDecor.setWindowBackground(background); final Drawable frame; if (mFrameResource != 0) { frame = getContext().getDrawable(mFrameResource); } else { frame = null; } mDecor.setWindowFrame(frame); mDecor.setElevation(mElevation); mDecor.setClipToOutline(mClipToOutline); if (mTitle != null) { setTitle(mTitle); } if (mTitleColor == 0) { mTitleColor = mTextColor; } setTitleColor(mTitleColor); } mDecor.finishChanging(); return contentParent; }
generateLayout这个方法里面的东西太多,我前面删除一部分
这里layoutResource,是根据我们设置的状态栏主题,判断DecorView自动为我们加载什么样的布局。当我们将主题设置为NoTitleBar时,generateLayout方法中的layoutResource变量值为R.layout.screen_simple,所以我们看下系统这个screen_simple.xml布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:orientation="vertical"> <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" android:theme="?attr/actionBarTheme" /> <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" /> </LinearLayout>
当拿到这个layoutResource之后,执行的是mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
//DecorView的onResourcesLoaded方法 void onResourcesLoaded(LayoutInflater inflater, int layoutResource) { mStackId = getStackId(); if (mBackdropFrameRenderer != null) { loadBackgroundDrawablesIfNeeded(); mBackdropFrameRenderer.onResourcesLoaded( this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable, mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState), getCurrentColor(mNavigationColorViewState)); } mDecorCaptionView = createDecorCaptionView(inflater); final View root = inflater.inflate(layoutResource, null); 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)); } mContentRoot = (ViewGroup) root; initializeElevation(); }
这里我们看到执行了一下mDecorCaptionView = createDecorCaptionView(inflater);
// Free floating overlapping windows require a caption. private DecorCaptionView createDecorCaptionView(LayoutInflater inflater) { DecorCaptionView decorCaptionView = null; for (int i = getChildCount() - 1; i >= 0 && decorCaptionView == null; i--) { View view = getChildAt(i); if (view instanceof DecorCaptionView) { // The decor was most likely saved from a relaunch - so reuse it. decorCaptionView = (DecorCaptionView) view; removeViewAt(i); } } final WindowManager.LayoutParams attrs = mWindow.getAttributes(); final boolean isApplication = attrs.type == TYPE_BASE_APPLICATION || attrs.type == TYPE_APPLICATION || attrs.type == TYPE_DRAWN_APPLICATION; // Only a non floating application window on one of the allowed workspaces can get a caption if (!mWindow.isFloating() && isApplication && StackId.hasWindowDecor(mStackId)) { // Dependent on the brightness of the used title we either use the // dark or the light button frame. if (decorCaptionView == null) { decorCaptionView = inflateDecorCaptionView(inflater); } decorCaptionView.setPhoneWindow(mWindow, true /*showDecor*/); } else { decorCaptionView = null; } // Tell the decor if it has a visible caption. enableCaption(decorCaptionView != null); return decorCaptionView; }
上面的代码我也没看懂,但是看结果是mDecorCaptionView的创建结果还是空,当mDecorCaptionView == null的时候会把我们加载出来的final View root = inflater.inflate(layoutResource, null);添加到DecorView的第0个位置,也就是一个FrameLayout视图最下面,就是是把刚才那个R.layout.screen_simple文件添加到DecorView最下面,也许看下面的Hierarchy View视图更直观一些,而且我们还看到了DecorView里面有一个LinearLayout并且index为0,细心的会发现DecorView还包含了NavigationBar View 还有StatusBar View,这些东西是什么时候加进去的呢?这个先不管,也可以自己去看DecorView的源码刨根问底。
现在我们在拐回到刚才的generateLayout方法
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
其实就是执行的getDecorView().findViewById,这个东西是谁呢?就是前面加载的R.layout.screen_simple文件里面的FrameLayout android:id=”@android:id/content”。最后contentParent作为generateLayout方法的返回赋值给mContentParent = generateLayout(mDecor);
/** * Finds a view that was identified by the id attribute from the XML that * was processed in {@link android.app.Activity#onCreate}. This will * implicitly call {@link #getDecorView} for you, with all of the * associated side-effects. * * @return The view if found or null otherwise. */ @Nullable public View findViewById(@IdRes int id) { return getDecorView().findViewById(id); }
写了一大圈我们再回到PhoneWindow的setContentView(int layoutResID)方法,执行mLayoutInflater.inflate(layoutResID, mContentParent);将我们Activity的R.layout.activity_main布局加载到mContentParent里面去,再看下面的图也许会更清晰一些。
回答几个问题
现在来回答几个疑问1,activity.findViewById(Window.ID_ANDROID_CONTENT)拿到的是谁?
答:本文中拿到的是R.layout.sample布局文件的FrameLayout,其实根据不同的主题拿到不同的布局文件中的FrameLayout,它用来存放我们setContentView(id)时加载的布局
2,activity.getWindow().getDecorView().getChildAt(0)拿到的是谁?
答:本文中拿到的是R.layout.sample布局文件,其实根据不同的主题设置拿到不同的布局文件
3,statusBar在哪里?
答:在DecorView里面,可以去看上面的Hierarchy View视图,也可以自己使用Hierarchy View看自己布局的视图更直观些。
参考文章
http://blog.csdn.net/fuuckwtu/article/details/6519689http://blog.csdn.net/yanbober/article/details/45970721
http://blog.csdn.net/mr_liabill/article/details/49534851
http://www.jianshu.com/p/983eb8d5bb1a
http://www.jianshu.com/p/afa921d8ed24
http://blog.csdn.net/hohohong/article/details/54412464
相关文章推荐
- PhoneWindow(关于setContentView)
- setContentView 与 Window、PhoneWindow及DecorView
- PhoneWindow.setContentView代码
- Android窗口机制(二)Window,PhoneWindow,DecorView,setContentView源码理解
- Window、View与setContentView()
- Activity中的Window的setContentView
- View编程(1): Window、View与setContentView()
- Window、View与setContentView()
- requestWindowFeature()为什莫要放在setContentView(0前面
- android Activity.setContentView和Window PhoneWindow类学习
- 关于使用RequestWindowFeature为啥一定要在setContentView之前调用
- Android为什么requestWindowFeature()在setContentView()之前调用?
- android之setContentView,addContentView(),Window,WindowManager,Dialog源码剖析。
- 为什么requestWindowFeature一定要在setContentView之前调用?
- Window、View与setContentView()
- Window、View与setContentView()
- android 从setContentView到window显示机制
- 为什么requestWindowFeature()方法要在setContentView()方法之前调用?
- android setContentView(R.layout.main) 语法错误
- 让tableView滚到顶部的功能,请注意UITabelView 继承自UIScrollView,而setContentOffset是scrollview