android View和ViewGroup如何在应用程序界面(窗口)中显示

参考 http://blog.csdn.net/yanbober/article/details/45970721

参考 上一篇LayoutInflater的学习

我们平时从手机上看到的一个界面,他们其实就是一系列的View对象由树状的形式组合成的View对象树视图。这些View对象被新建出来后,他们会根据自己的绘制规则将自己绘制成视图形式,绘制完毕后由Activity线程将他们整体显示出来。(看完文章后的理解,纯粹为了理解,并没检验正假。。。。反正只有自己看)。下面以及参考链接主要讲的是我们这个View对象树的结构,以及形成原理。


先说结构

看见上面图没?Activity中有一个成员为Window,其实例化对象为PhoneWindow,PhoneWindow为抽象Window类的实现类。

这里先简要说明下这些类的职责:

  1. Window是一个抽象类,提供了绘制窗口的一组通用API。

  2. PhoneWindow是Window的具体继承实现类。而且该类内部包含了一个DecorView对象,该DectorView对象是所有应用窗口(Activity界面)的根View。

  3. DecorView是PhoneWindow的内部类,是FrameLayout的子类,是对FrameLayout进行功能的修饰(所以叫DecorXXX),是所有应用窗口的根View 。

就上面DectorView  PhoneWindow  Activity我自己的理解是,DectorView就好比是一个相框,PhoneWindow就是管理着相框的相册,而Activity就是拥有相册的主人(我们的相册比较高级,装的是相框),那么相片是什么?相片就是我们主要显示的内容,而将相片装载到相框里我们使用的是setContentView方法。

     详细的代码看参考的链接吧

      我们分析这种常用的这种类型的setContentView方法



    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();
        }
    }
方法一开始就有这段代码
if (mContentParent == null) {
            installDecor();}

mContentParent是什么?它是一个容纳我们layoutID对应View或ViewGroup的容器ViewGroup,一开始它当然为空,然后我们执行installDector();

 private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor();
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        }
        if (mContentParent == null) {
            //根据窗口的风格修饰,选择对应的修饰布局文件,并且将id为content的FrameLayout赋值给mContentParent
            mContentParent = generateLayout(mDecor);
            //......
            //初始化一堆属性值
        }
    }

 一开始mDecor = generateDecor()仅仅是初始化一个的ViewDecor对象。

然后看方法

mContentParent = generateLayout(mDecor);

protected ViewGroup generateLayout(DecorView decor) {
        // Apply data from current theme.

        TypedArray a = getWindowStyle();

        //......
        //依据主题style设置一堆值进行设置

        // Inflate the window decor.

        int layoutResource;
        int features = getLocalFeatures();
        //......
        //根据设定好的features值选择不同的窗口修饰布局文件,得到layoutResource值

        //把选中的窗口修饰布局文件添加到DecorView对象里,并且指定contentParent值
        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);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }

        //......
        //继续一堆属性设置,完事返回contentParent
        return contentParent;
    }

首先根据features的值设置layoutResoutce的值,代码中省略这一步,然后View in = mLayoutInflater.inflate(layoutResource, null);生成相应的View

接着mdecor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));将in这个View添加到mdecor.

ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);在mdecor中找到ID为ID_ANDROID_CONTENT这个布局设置为contentParent,并将其返回个mContentParent

mContentRoot = (ViewGroup) in;这个就是我们layoutResource对应的View,它也是contentParent的父容器。

回到setContentView方法

mLayoutInflater.inflate(layoutResID, mContentParent);将我们加载的页面添加到mContentParent。

所以为什么我们layoutResID对应布局的layout_....属性得到正常显示,以为我们使用的是mLayoutInflater.inflate(layoutResID, mContentParent);

最后总的层次结构

phoneWindow--ViewDecor--mRootContent--mContentParent--我们自己的布局文件









阅读更多
上一篇android LayoutInflater学习
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭