Android GUI之Activity、Window、View

  相信大家在接触Android之初就已经知道了Activity中的setContentView方法的作用了,很明显此方法是用于为Activity填充相应的布局的。那么,Activity是如何将填充的布局绘制出来的呢?实际上Activity将View的绘制与显示交给了Window对象来处理,下面我们通过源码来进行跟踪分析。

  Activity的源码如下,只给出我们关注的部分:

public class Activity extends ContextThemeWrapperimplements LayoutInflater.Factory2,Window.Callback, KeyEvent.Callback,OnCreateContextMenuListener, ComponentCallbacks2,Window.OnWindowDismissedCallback {…………private Window mWindow;private WindowManager mWindowManager;…… /*** 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;}……/*** 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);initWindowDecorActionBar();}/*** Set the activity content to an explicit view. This view is placed* directly into the activity's view hierarchy. It can itself be a complex* view hierarchy. When calling this method, the layout parameters of the* specified view are ignored. Both the width and the height of the view are* set by default to {@link ViewGroup.LayoutParams#MATCH_PARENT}. To use* your own layout parameters, invoke* {@link #setContentView(android.view.View,android.view.ViewGroup.LayoutParams)}* instead.** @param view The desired content to display.** @see #setContentView(int)* @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)*/public void setContentView(View view) {getWindow().setContentView(view);initWindowDecorActionBar();}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, IVoiceInteractor voiceInteractor) {attachBaseContext(context);mFragments.attachActivity(this, mContainer, null);mWindow = PolicyManager.makeNewWindow(this);……}……}

PolicyManager的部分源码:

public final class PolicyManager {……private static final IPolicy sPolicy;static {// Pull in the actual implementation of the policy at run-time……sPolicy = (IPolicy)policyClass.newInstance();……}// Cannot instantiate this classprivate PolicyManager() {}// The static methods to spawn new policy-specific objectspublic static Window makeNewWindow(Context context) {return sPolicy.makeNewWindow(context);}……}

Policy的部分源码:

public class Policy implements IPolicy { ……public Window makeNewWindow(Context context) {return new PhoneWindow(context);}……}

  从给出的源码我们可以看到,Activity内部含有一个Window类型的对象mWindow,当我们调用setContentView方法时,实际上是委托给了Window对象进行处理。Window本身是一个抽象类,它描述了android窗口的基本属性和行为特征。在activity的attach方法中通过mWindow = PolicyManager.makeNewWindow(this)创建了Window对象。通过追踪代码可知, PolicyManager.makeNewWindow(this)实际上是调用Policy中的makeNewWindow方法,在此方法中创建了一个PhoneWindow对象。而PhoneWindow正是Window的子类。他们的关系图如下:

继续追踪源码,PhoneWindow对Window的抽象方法setContentView(int layoutResId)进行了实现,具体源码如下:

@Overridepublic 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);}final Callback cb = getCallback();if (cb != null && !isDestroyed()) {cb.onContentChanged();}}

  在这个方法中我们可以看到首先对mContentParent进行了判断,如果为空的话则调用installDecor方法,通过hasFeature判断window是否具备某些特征,如果窗口不含有FEATURE_CONTENT_TRANSITIONS特征,则清空mContentParent中的所有子元素,为后面加载布局文件到mContentParent中做好准备。通过后面的判断,我们也可以看出无论走那个分支,,其实都是对mContentParent布局内容做了更新。由此我们可以推断出mContentParent其实就是我们自己的布局的存放容器,它在PhoneWindow中定义如下:

// This is the view in which the window contents are placed. It is either// mDecor itself, or a child of mDecor where the contents go.private ViewGroup mContentParent;

  那么mContentParent是在哪里被创建的呢,很显然是在方法installDecor中,方法installDecor的关键代码如下:

private void installDecor() {if (mDecor == null) {mDecor = generateDecor();……}if (mContentParent == null) {mContentParent = generateLayout(mDecor);……}}

而做人的能力则会给你一百种机会。

Android GUI之Activity、Window、View

相关文章:

你感兴趣的文章:

标签云: