Android窗口机制(一)Window,PhoneWindow,DecorView,setContentView源码理解

Activtiy 视图

Window,Activity,DecorView之间的关系_第1张图片

Activity —> PhoneWindw —> DecorView —>(TitleView — ContentView)

1.Window

public abstract class Window {    ...    @Nullable    public View findViewById(@IdRes int id) {        return getDecorView().findViewById(id);    }     public abstract void setContentView(@LayoutRes int layoutResID);     ...}

Window 是 顶级窗口外观和行为策略的抽象基类。应该将此类的实例用作添加到窗口管理器的顶级视图。它提供标准的UI策略,例如背景,标题*区域,默认密钥处理等,其本身是一个抽象类 PhoneWinodw 是其唯一一个实现类

2.PhoneWinodw

Window 的唯一实现类

public class PhoneWindow extends Window implements MenuBuilder.Callback {    private final static String TAG = "PhoneWindow";      // 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 ViewGroup mContentRoot;    ...}

在PhoneWindow中 有一个DecorView 内部类 DecorView 是窗口的顶层视图,包含窗口装饰

private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {        /* package */int mDefaultOpacity = PixelFormat.OPAQUE;        /** The feature ID of the panel, or -1 if this is the application's DecorView */        private final int mFeatureId;        private final Rect mDrawingBounds = new Rect();        private final Rect mBackgroundPadding = new Rect();        private final Rect mFramePadding = new Rect();        private final Rect mFrameOffsets = new Rect();        .... }

DecorView 继承于FrameLayout 既然继承于FrameLayout ,我们就可以在DecorView 中添加布局 等等 而DecorView则是由PhoneWindow负责添加

那么两者又没什么关系,我们看到布局又是如何添加到 DecorView中

我们在初始化Activity时候,需要setContentView(xx); 设置Activity布局,那么两者应该有关系,我们先看看setContentView源码

public void setContentView(@LayoutRes int layoutResID) {        getWindow().setContentView(layoutResID);        initWindowDecorActionBar();    }

源码中 调用了 getWindow().setContentView(xxx); 方法 getWIndow 获取的就是 当前Activtiy的 window

-----划重点

mContentParent 这是放置窗口内容的视图。它可以是// mDecor本身,也可以是内容的mDecor的子项。

整个PhoneWindw 中重点mContentParent 我们所有 获取到的 视图都赋值给 mContentParent,再由CallBack 通知界面刷新

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

首先我们看看setContentView源码

在if判断中 先判断了mContentParent 是否为空 如果不为空 并且 当前是否加载过过场动画 加载过了 直接进行 remove

如果mContentParent 不为空 调用了 installDecor(); 方法

installDecor 方法 有点多 我们只看 核心代码

 private void installDecor() {        mForceDecorInstall = false;        //在这里 判断了DecorView是否为空 为空则进行 进一步的创建        if (mDecor == null) {            mDecor = generateDecor(-1);            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);            mDecor.setIsRootNamespace(true);            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);            }        } else { // 否则设置当前DecorView            mDecor.setWindow(this);        }        //当前mContentParent 不为空        if (mContentParent == null) {            //把当前的 decorView 添加到Window 绑定到 布局上            mContentParent = generateLayout(mDecor);            // Set up decor part of UI to ignore fitsSystemWindows if appropriate.            mDecor.makeOptionalFitsSystemWindows();            //~~~~省略 加载 动画代码        } }

DecorView的创建 通过generateDecor方法创建

protected DecorView generateDecor(int featureId) {        Context context;        if (mUseDecorContext) {            Context applicationContext = getContext().getApplicationContext();            if (applicationContext == null) {                context = getContext();            } else {                context = new DecorContext(applicationContext, getContext().getResources());                if (mTheme != -1) {                    context.setTheme(mTheme);                }            }        } else {            context = getContext();        }        return new DecorView(context, featureId, this, getAttributes());    }

下面我们看 布局是如何进行绑定的generateLayout()

//返回值为ViewGroupprotected ViewGroup generateLayout(DecorView decor) {        //设置主题 根据自己的设置的 主题来加载         TypedArray a = getWindowStyle();        if (false) { // 省略不看            System.out.println("From style:");            String s = "Attrs:";            for (int i = 0; i < R.styleable.Window.length; i++) {                s = s + " " + Integer.toHexString(R.styleable.Window[i]) + "="                        + a.getString(i);            }            System.out.println(s);        }        //是否全屏        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);        }        //根据设置的的主题 来进行判断        if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {            requestFeature(FEATURE_NO_TITLE);        } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {            // Don't allow an action bar if there is no title.            requestFeature(FEATURE_ACTION_BAR);        }        //以下大部分代码。都是 设置主题 资源等等 直接省略        // Inflate the window decor.        int layoutResource;        int features = getLocalFeatures();        // 根据 用户设置的。主题 来设置 对应的features。在根据features 来加载对应的 布局文件         if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {            layoutResource = R.layout.screen_swipe_dismiss;        } else if(){          //省略          .........        }        mDecor.startChanging();        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);        //吧当前的视图设置到 DecorView 中         // public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);        if (contentParent == null) {            throw new RuntimeException("Window couldn't find content container view");        }    return conentParent;}

generateLayout 方法 主要 根据 用户设置的。主题 来设置 对应的features。在根据features 来加载对应的 布局文件

不同的布局 样子不同 比如没有 标题栏 等等 。 DecorView 布局 只有一个子元素为LinearLayout。代表整个Window界面,包含通知栏,标题栏,内容显示栏三块区域。其中我们的 内容都设置在了 id为 content 的 FrameLayout 中

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:fitsSystemWindows="true"    android:orientation="vertical">    <ViewStub android:id="@+id/action_mode_bar_stub"              android:inflatedId="@+id/action_mode_bar"              android:layout="@layout/action_mode_bar"              android:layout_width="match_parent"              android:layout_height="wrap_content"              android:theme="?attr/actionBarTheme" />    <FrameLayout         android:id="@android:id/content"         android:layout_width="match_parent"         android:layout_height="match_parent"         android:foregroundInsidePadding="false"         android:foregroundGravity="fill_horizontal|top"         android:foreground="?android:attr/windowContentOverlay" /></LinearLayout>

generateLayout 方法返回了 conentParent 也就回到了我们最开始的方法 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);        }       //刷新 Callback 通知        mContentParent.requestApplyInsets();        final Callback cb = getCallback();        if (cb != null && !isDestroyed()) {            cb.onContentChanged();        }        mContentParentExplicitlySet = true;    }    //callback 通知 callback Window下面的方法    public class Activity extends ContextThemeWrapper        implements LayoutInflater.Factory2,        Window.Callback, KeyEvent.Callback,        OnCreateContextMenuListener, ComponentCallbacks2,        Window.OnWindowDismissedCallback { ... }

小结:

1.Window是一个抽象类,提供了各种窗口操作的方法

2.PhoneWindow则是Window的唯一实现类,它里面实现了各种添加背景主题ContentView的方法,内部通过DecorView来添加顶级视图

3.DecorView,顶级视图,继承与FramentLayout,setContentView则是添加在它里面的@id/content里

4.setContentView里面创建了DecorView,根据Theme,Feature添加了对应的布局文件

5.当setContentView设置显示后会回调Activity的onContentChanged方法

本文部分内容来源于https://www.jianshu.com/p/e42b638944ae 感谢作者

更多相关文章

  1. Android Dialog触摸对话框外部让其消失的实现方法
  2. android 获取当前时间的方法
  3. Android ScrollView嵌套ViewPager不显示和出现空白部分 解决方法
  4. Android查询所有联系人和根据号码查询联系人方法
  5. Android系统信息查看方法
  6. Android裁剪图像实现方法示例
  7. Android中常用的bitmap处理方法
  8. android系统自带的适配布局activity_list_item

随机推荐

  1. Flutter PageView使用案例
  2. Android中SQLite数据库介绍和使用
  3. android万能的Listview适配器
  4. Content Provider 基础
  5. Android(安卓)分享实现,微信、微博、腾讯
  6. Android(安卓)IPC进程间通信详解最新Andr
  7. android实习生面经
  8. PHP变量数据类型,变量和常量的定义以及数
  9. 今日报错系列:未定义的SYSTEMTIME
  10. 什么最宝贵呢?