转载

Android GUI之Activity、Window、View

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

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

public class Activity extends ContextThemeWrapper         implements 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 class     private PolicyManager() {}     // The static methods to spawn new policy-specific objects     public 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的子类。他们的关系图如下:

Android GUI之Activity、Window、View

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

@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);  }  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);             ……        } }

在这个方法中,我们可以看到,首先对mDecor进行判断,如果为空在调用generateDecor方法生成mDecor对象,那么mDecor对象是什么呢?通过查看代码,可以知道mDecor的类型为DecorView,此类型是定义在PhoneWindow中的一个内部类,它继承了FrameLayout。紧接着判断mContentParent是否为空,为空则调用generateLayout并通过传入参数mDecor生成了mContentParent对象。在这个方法中通过应用的主题、窗口特征等来确定使用的布局资源并将使用的布局添加mDecor中,而这些布局中都会含有一个id为content的ViewGroup(FrameLayout),此ViewGroup正是mContentParent,方法关键代码如下:

protected ViewGroup generateLayout(DecorView decor) {  ……  View in = mLayoutInflater.inflate(layoutResource, null);  decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));  mContentRoot = (ViewGroup) in;  ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);    ……  return contentParent; } 

由此我们可以确定,view的显示处理顺序为Activity->PhoneWindow->DecorView->ViewGroup(mContentView)->自定义的View(布局)。

Activity中显示视图的层次结构,具体如下:

Android GUI之Activity、Window、View

疑问咨询或技术交流,请加入官方QQ群: Android GUI之Activity、Window、View (452379712)

作者: 杰瑞教育

出处: http://www.cnblogs.com/jerehedu/

本文版权归 烟台杰瑞教育科技有限公司 和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

正文到此结束
Loading...