Activity Window View-[Android_Ya

作者: Android_YangKe | 来源:发表于2018-04-22 14:14 被阅读206次

    转载请注明 Android_YangKe,谢谢!

    老实说自己做 Android 有一段时间了,但发现 Android 技能提升上有了点小瓶颈,总感觉自己什么都会,又都感觉自己什么都不会,于是就有了此片文章。

    下面我们将通过 Window 慢慢引出三者之间的关系,同时适当的源码辅助分析。说到源码相信很多人都是心中都一万个 mmb,劳资这么差的英文,动不动成千上万行的代码,脑袋瞬间短路好不好... 微笑-微笑 。

    Activity,View 是什么我相信不用解释,而 Window 在使用频率上相对来说就低了些,那么 Window 是什么?在 Android 体系中扮演着什么样的角色?下面是 Google 工程师对 Window 类的一些介绍,我们来看下。

    //Window.java
    /**
     * 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.
     *
     * <p>The only existing implementation of this abstract class is
     * android.view.PhoneWindow, which you should instantiate when needing a
     * Window.
     */
    

    大致意思:Window 是一个基础类,是顶级视图的承载。提供了标准的 UI 策略,如背景,标题。同时 Window 只有一个实现类PhoneWindow。官方的解释还是比较给力,很详细的介绍了该对象,现在我们对 Window 有了一个初步认识。下面我们将结合具体案例进行探索。

    Activity 中 onCreate 函数相信大家再熟悉不过了,如我们将里面的setContentView函数注释掉时,会发现原来我们的炫酷的页面只留下一个标题加空白页面(前提我们没有修改默认的主题),这么看来此行代码很关键。

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

    一般来说我们的 Activity 都是继承自 AppCompatActivity,但最终的顶层父类是 Activity。通过上面源码我们可以发现 Activity 只是 View 宿主,并没有真正的实现setContentView而是通过 getWindow 操作此函数,我们来看下 getWindow。

    //Activity.java
    public Window getWindow() {
        return mWindow;
    }
    final void attach(...) {
        attachBaseContext(context);
        mWindow = new PhoneWindow(this, window,activityConfigCallback);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        ...
    }
    

    很简单 getWindow 函数返回了一个实例PhoneWindow。由于代码中没有对 getWindow 判空,我们可以推测出 attach 函数一定在 setContentView 之前执行,也就是 onCreate 函数之前,不然一定会报NullPointerException

    扯得有点多,言归正传。这里我们看到了 Window 的身影,从上文我们了解到 Window 是顶级视图的承载,而PhoneWindow又是 Window 的唯一子类,我们尝试着去 PhoneWindow中探索 setContentView

    //PhoneWindow.java
    
    // 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.
    ViewGroup mContentParent;
    
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }
        ...
    }
    

    一大串代码(当然我省略了一部分代码)果不其然,真正的操作的就是在 Window 的 setContentView函数中完成的,首先。

    • mDecor:Window 上最顶层的视图,如果按照从上到下的顺序,分别是状态栏,标题栏,具体 View 控件。由于其继承了 FrameLayout 也就是说 mDecor 也是一个 ViewGroup。
    • mContentParent:表示视图区域,里面可以是 mDecor,也可以是 mDecor 中的具体 View,但不代表具体视图。

    了解了这两个对象的作用,我们继续看代码。removeAllViews函数顾名思义,也就是从当前 Window 中移除所有 View,这里就不进入源码进行分析了(代码水很深,随时保留精气神)。现在就剩下installDecor了,此函数貌似很关键,我们跟进来看下。

    //PhoneWindow.java
    private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor(-1);
            ...
        } else {
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
        ...
        }
    }
    

    首先映入眼帘的是generateDecor,同时此函数的返回值赋予了 mDecor。随后系统调用了generateLayoutgenerateLayout从字面意义上来看是初始化布局,莫非跟布局 View 有关,我们来看一下。

    //PhoneWindow.java
    protected ViewGroup generateLayout(DecorView decor) {
     // Apply data from current theme.
     TypedArray a = getWindowStyle();
     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);
     }
     if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {
         setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
     }
     ...
    }      
    

    这里我们抛去 if 的各种判断直接看这几个常量:FLAG_FULLSCREENFEATURE_NO_TITLEFLAG_FULLSCREEN等等,是不是似曾相识呢?相信大家在 onCreate 函数setContentView前都有过重设当前 Activity 样式的经历,严格来说应该是 Window (Activity 全屏、去除标题栏)。也就是说屏幕上页面的尺寸,样式都是通过 Window 来控制的!看来此函数很重要,打起精神,我们再来分析一波。

    //PhoneWindow.java
    protected ViewGroup generateLayout(DecorView decor) {
        // Remaining setup -- of background and title -- that only applies
        // to top-level windows.
    
        WindowManager.LayoutParams params = getAttributes();
        if (!hasSoftInputMode()) {
            params.softInputMode = a.getInt(R.styleable.Window_windowSoftInputMode, params.softInputMode);
        }
        if (params.windowAnimations == 0) {
            params.windowAnimations = a.getResourceId(R.styleable.Window_windowAnimationStyle, 0);
        }
        ...
        if (getContainer() == null) {
            final Drawable background;
            if (mBackgroundResource != 0) {
                background = getContext().getDrawable(mBackgroundResource);
            } else {
                background = mBackgroundDrawable;
            }
            mDecor.setWindowBackground(background);
    
            final Drawable frame;
            if (mFrameResource != 0) {
                frame = getContext().getDrawable(mFrameResource);
            } else {
                frame = null;
            }
            mDecor.setWindowFrame(frame);
            ...
            if (mTitle != null) {setTitle(mTitle);}
            if (mTitleColor == 0) {mTitleColor = mTextColor;}
            setTitleColor(mTitleColor);
        }
        mDecor.finishChanging();
    
        return contentParent;
    }
    

    首先我们看下官方给的注释:

    • Remaining setup -- of background and title -- that only applies to top-level windows.

    将自己置身于情境之中,大概意思:设置标题,背景颜色,将视图应用在顶层 View 上,谁呢?DecorView。

    其次:
    获取 Window 的一些属性,并根据情况对输入法模式、载入动画等等进行配置。

    最后:
    对 Window 承载的视图进行背景设置,边距设置,标题栏颜色设置,标题栏设置等等。

    到这里相信你对 Window 的作用,以及其在 Android 中扮演着怎样的角色有了一定的认知。下面我们来看一张图片,辅助我们理解 Activity、Window、View 之间的关系:(图片源自互联网,出处不明)

    yangke.png

    最外层是 Activity、其次是 PhoneWindow、最后是我们常操作的 View 控件。通过这张图相信你对 Activity、Window、View 有了进一步的认识,下面我们再来重新认识下三者。

    View:说到 View 相信大家都很熟悉,像什么 TextView、Button、ImageView 等等我相信你可以一口气说一大堆。View 是具体视图的最小展示单元。在页面上可以是一张图片,一段文本,一个网页。简而言之:View 就是具体视图(常常与用户打交道)。

    Window:我们真正意义上所说的页面,是 Activity 任命的大使,主要用于管理 View,设置窗口(视图)样式、尺寸、输入法模式等。我们也可以通过WindowManager对 View 进行添加和移除操作等。简而言之:Window 主要用于控制显示窗口的尺寸、样式、View 的移除添加(常常与研发人员打交道)。

    Activity:页面的载体。维护应用程序的生命周期、提供用户处理事件的 API、进程间通信等。简而言之:用于管理系统组件相关事物。

    整理:
    Activity-> PhoneWindow-> DecorView。

    总结:
    Activity 作为四大组件主要与系统进行交互,用于组件间通信,生命周期管理、进程通信等。Window 作为视图载体,主要用于管理视图的尺寸、样式、输入法模式、View 的移除添加等,需要依托于 Activiity。View 就比较简单了,不同的 View 用于展示不同的视图,例:文本组件,图片组件,甚至网页等,需要依托于 Window。也就是说三者相辅相成,谁离开都将无存在意义。

    尾声:
    为什么 View 不直接与 Activity 关联呢?反而又设计出一个 Window 对象?

    其实这里有些面向对象的概念,也就是说对于庞大的功能我们需要进行拆分,让其尽量独立,各司其职,同时在功能互不影响的情况下,相辅相成。

    完~

    喜欢有帮助的话: 双击、评论、转发,动一动你的小手让更多的人知道!

    相关文章

      网友评论

      本文标题:Activity Window View-[Android_Ya

      本文链接:https://www.haomeiwen.com/subject/mjmikftx.html