标签:
整个过程基本上都在PhoneWindow中完成的。先罗列里面会用到的方法。如下:
//可以看出它mDecor为DecorView对象 protected DecorView generateDecor() { return new DecorView(getContext(), -1); } //findViewById()如下: public View findViewById(@IdRes int id) { return getDecorView().findViewById(id); } //获取theme中关于window属性的设置 public final TypedArray getWindowStyle() { synchronized (this) { if (mWindowStyle == null) { mWindowStyle = mContext.obtainStyledAttributes( com.android.internal.R.styleable.Window); } return mWindowStyle; } } public final TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) { return getTheme().obtainStyledAttributes(attrs); } //是否有指定的feature。 public boolean hasFeature(int feature) { return (getFeatures() & (1 << feature)) != 0; } //设置feature。上面的getFeatures()只是返回mFeatures public boolean requestFeature(int featureId) { final int flag = 1<<featureId; mFeatures |= flag; mLocalFeatures |= mContainer != null ? (flag&~mContainer.mFeatures) : flag; return (mFeatures&flag) != 0; }
调用Activity#setContentView()最终会调用到PhoneWindow#setContentView。其代码如下:
@Override public void setContentView(int layoutResID) { 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); } final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } }首先看installDecor的作用:创建一个DecorView对象,并赋值给mDecor。同时为mContentParent进行赋值。
最后会调用onContentChanged()回调。
整体逻辑就这么多,关键是要看mContentParent指的是什么——因为它是我们写的布局的父类。
因为在installDecor()中会调用generateLayout(mDecor)生成mContentParent,所以先看generateLayout(mDecor)方法。
其方法看着比较多,但实质就是根据该activity的theme找到对应的布局,inflate该布局并返回id为android:id/content的ViewGroup。
protected ViewGroup generateLayout(DecorView decor) { TypedArray a = getWindowStyle();// Apply data from current theme. //一个输出语句,省略 //获取theme中的windowIsFloating属性 mIsFloating = a.getBoolean(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); } //根据指定的属性调用requestFeatures()设置一些feature和flags,具体代码略 if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) { requestFeature(FEATURE_NO_TITLE);//无标题feature } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) { requestFeature(FEATURE_ACTION_BAR);// Don't allow an action bar if there is no title. } //略了一部分代码 if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) { requestFeature(FEATURE_SWIPE_TO_DISMISS);//侧滑返回。但很可惜api21以后才能用,而且还有bug } //略了一部分setFlags与requestFeature代码,它们都是根据a中设置的一些属性值 int layoutResource; int features = getLocalFeatures(); //根据features值获取layoutResourece的值,它代表一个布局的id。代码略 mDecor.startChanging(); //添加布局,并将布局添加到decor上。 View in = mLayoutInflater.inflate(layoutResource, null); decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); mContentRoot = (ViewGroup) in; //获取添加自己View的viewgroup,layoutResource会有好几个,但都会含义一个id为@android:id/content的ViewGroup,例如可以见下面的screen_swipe_dismiss.xml ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); //一些关于features进行的设置 mDecor.finishChanging(); return contentParent; }具体的解释上注释中。其中ID_ANDROID_CONTRENT定义如下:
/** * The ID that the main layout in the XML layout file should have. */ public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
从这个注释中我们还可以看出contentParent的作用。
private void installDecor() { if (mDecor == null) { //生成mDecor,并进行一系列的设置 mDecor = generateDecor(); mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);//子View不获取焦点时,它才获取焦点 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(); //mDecor是一个ViewGroup,这里省略的就是对其中的某些子View进行初始化设置的代码 if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) { mDecor.setBackgroundFallback(mBackgroundFallbackResource); } // Only inflate or create a new TransitionManager if the caller hasn't // already set a custom one. //一些关于TransitionManager的初始化工作 } }主要过程分为两部分:生成mDecor。
根据mDecor,通过generateLayout(mDecor)生成mContentParent。然后对mContentParent进行一系列设置。
上面就是整个过程的分析:
先初始化DecorView,然后根据设置的features不同为decorView加载不同的布局。
接着在decorView中find一个id为android:id/content的mContentParent——这即是自己布局的父容器。
最后通过LayoutInflater.inflate将自己的布局添加到mContentParent中。
一个decorview加载的布局:
<com.android.internal.widget.SwipeDismissLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/content" android:fitsSystemWindows="true" android:layout_width="match_parent" android:layout_height="match_parent" />从中可以看出,它的确有一个id为android:id/content的ViewGroup。
标签:
原文地址:http://blog.csdn.net/u010410408/article/details/51331159