decorView和window之间的层级及关系

转载请注明出处:http://blog.csdn.net/guxiao1201/article/details/41744107

 

首先贴出实现Activity对话框圆角的核心代码

@Override
    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        DisplayMetrics dm = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(dm);
        View view = getWindow().getDecorView();
        WindowManager.LayoutParams lp = (WindowManager.LayoutParams)view.getLayoutParams();
        lp.gravity = Gravity.CENTER;
        lp.width = (dm.widthPixels * 4) / 5;
        lp.height = (dm.widthPixels * 4) / 5;
        getWindowManager().updateViewLayout(view,lp);
        getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        view.setBackgroundResource(R.drawable.dialog_activity_bg);
    }
在上篇博客onAttachedToWindow()在整个Activity生命周期的位置及使用中解释了为什么在onAttachedToWindow中修改窗口尺寸,上面代码最后两行分别对window和decorView设置背景,那么问题来了,一个窗体中decorView和window之间的关系是什么?

通过文章开始贴出的代码View view = getWindow().getDecorView();就可以对Window和DecorView的层级关键猜测一二,decorView是否作为一个变量由window维护?
和之前思路一样,想探究这个问题就得看源码说话。这里依然参考老罗的博客
Android应用程序窗口(Activity)的视图对象(View)的创建过程分析

既然猜测decorView是window的变量,那么就先找到window和activity之间的关系。通过源码发现

public class Activity extends ContextThemeWrapper  
        implements LayoutInflater.Factory,  
        Window.Callback, KeyEvent.Callback,  
        OnCreateContextMenuListener, ComponentCallbacks {  
    ......  
  
    private Window mWindow;  
    ......  
  
    public Window getWindow() {  
        return mWindow;  
    }  
    ......  
  
    public void setContentView(int layoutResID) {  
        getWindow().setContentView(layoutResID);  
    }  
  
    ......  
}  
Window原来是Activity的一个变量,可以通过getWindow()获取,而且Activity中经常用到的setContentView原来调用的是window的setContentView。那么Window是什么?在Activity中起什么作用?OK~带着问题再查阅Google的官方文档。

Abstract base class for a top-level window look and behavior policy. An instance of this class should be used as the top-level view added to the window manager. It provides standard UI policies such as a background, title area, default key processing, etc.

The only existing implementation of this abstract class is android.policy.PhoneWindow, which you should instantiate when needing a Window. Eventually that class will be refactored and a factory method added for creating Window instances without knowing about a particular implementation.

Google说Window是WindowManager最顶层的视图,它负责背景(窗口背景)、Title之类的标准的UI元素,Window是一个抽象类,整个Android系统中PhoneWindow是Winodw的唯一实现类。所以接下来进入PhoneWinodw一探究竟。

public class PhoneWindow extends Window implements MenuBuilder.Callback {  
    ......  
  
    // This is the top-level view of the window, containing the window decor.  
    private DecorView mDecor;  
    ......  
  
    // 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;  
    ......  
  
    private TextView mTitleView;  
    ......  
  
    private CharSequence mTitle = null;  
    ......  
  
    private void installDecor() {  
        if (mDecor == null) {  
            mDecor = generateDecor();  
            ......  
        }  
        if (mContentParent == null) {  
            mContentParent = generateLayout(mDecor);  
  
            mTitleView = (TextView)findViewById(com.android.internal.R.id.title);  
            if (mTitleView != null) {  
                if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {  
                    View titleContainer = findViewById(com.android.internal.R.id.title_container);  
                    if (titleContainer != null) {  
                        titleContainer.setVisibility(View.GONE);  
                    } else {  
                        mTitleView.setVisibility(View.GONE);  
                    }  
                    if (mContentParent instanceof FrameLayout) {  
                        ((FrameLayout)mContentParent).setForeground(null);  
                    }  
                } else {  
                    mTitleView.setText(mTitle);  
                }  
            }  
        }  
    }  
  
    ......  
}  
在这里总算找到了DecorView,和上面猜测的一样,DecorView确实为Window的变量。同时还发现一个名为mContentParent的ViewGroup,那么这个变量的作用是什么?和DecorView有什么关系?带着问题接着往下看。

PhoneWindow的setContentView方法

@Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mLayoutInflater.inflate(layoutResID, mContentParent);
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }
在mContentParent为null时会调用installDecor()来创建应用程序窗口视图对象。接着在installDecor()中调用generateLayout为mContentParent赋值。

    protected ViewGroup generateLayout(DecorView decor) {
        // 获取中的theme属性或者代码requestWindowFeature()中指定的Features

        TypedArray a = getWindowStyle();

        if (false) {
            System.out.println("From style:");
            String s = "Attrs:";
            for (int i = 0; i < com.android.internal.R.styleable.Window.length; i++) {
                s = s + " " + Integer.toHexString(com.android.internal.R.styleable.Window[i]) + "="
                        + a.getString(i);
            }
            System.out.println(s);
        }

        mIsFloating = a.getBoolean(com.android.internal.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);
        }

        //...


        // Inflate the window decor.

        int layoutResource;
        int features = getLocalFeatures();
        // System.out.println("Features: 0x" + Integer.toHexString(features));
        if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = com.android.internal.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 = com.android.internal.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(
                        com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = com.android.internal.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(
                        com.android.internal.R.attr.dialogTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
                layoutResource = com.android.internal.R.layout.screen_action_bar;
            } else {
                layoutResource = com.android.internal.R.layout.screen_title;
            }
            // System.out.println("Title!");
        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
            layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode;
        } else {
            // Embedded, so no decoration is needed.
            layoutResource = com.android.internal.R.layout.screen_simple;
            // System.out.println("Simple!");
        }

        mDecor.startChanging();

        View in = mLayoutInflater.inflate(layoutResource, null);
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));

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

        // Remaining setup -- of background and title -- that only applies
        // to top-level windows.
        if (getContainer() == null) {
            Drawable drawable = mBackgroundDrawable;
            if (mBackgroundResource != 0) {
                drawable = getContext().getResources().getDrawable(mBackgroundResource);
            }
            mDecor.setWindowBackground(drawable);
            drawable = null;
            if (mFrameResource != 0) {
                drawable = getContext().getResources().getDrawable(mFrameResource);
            }
            mDecor.setWindowFrame(drawable);

            // System.out.println("Text=" + Integer.toHexString(mTextColor) +
            // " Sel=" + Integer.toHexString(mTextSelectedColor) +
            // " Title=" + Integer.toHexString(mTitleColor));

            if (mTitleColor == 0) {
                mTitleColor = mTextColor;
            }

            if (mTitle != null) {
                setTitle(mTitle);
            }
            setTitleColor(mTitleColor);
        }

        mDecor.finishChanging();

        return contentParent;
    }
上面代码可看到将一些页面特性布局,例如ActionBar、Title等添加到decorView中,并且根据代码ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);可看到原来mContentParent是id="@android:id/content"的ViewGroup。再回到PhoneWindow.setContentView
mLayoutInflater.inflate(layoutResID, mContentParent);
原来我们自定义的View都是mContentParent的子View。并且上面这段代码还发现了为什么必须在setContentView之前才能执行requestWindowFeature的彩蛋。

根据上面所有的猜测和分析,最终发现Window、DecorView和mContentParent的层级关系如下图(如有误请指正)

decorView和window之间的层级及关系_第1张图片


参考资料:

Window窗口布局--DecorView浅析

Android应用程序窗口(Activity)的视图对象(View)的创建过程分析

你可能感兴趣的:(decorView和window之间的层级及关系)