Android GUI之Activity、Window、View

来源:互联网 发布:淘宝店托管公司 编辑:程序博客网 时间:2024/06/03 23:42

  相信大家在接触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的子类。他们的关系图如下:

继续追踪源码,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中显示视图的层次结构,具体如下:

 

  疑问咨询或技术交流,请加入官方QQ群:JRedu技术交流 (452379712)

 

作者:杰瑞教育
出处:http://blog.csdn.net/jerehedu/ 
本文版权归烟台杰瑞教育科技有限公司和CSDN共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
 
1 0
原创粉丝点击