android setContentView

来源:互联网 发布:Tensorflow 字符识别 编辑:程序博客网 时间:2024/05/24 01:11

http://blog.csdn.net/yanbober/article/details/46128379
一下是对上面文章的总结

Acitivity的setContentView

  public void setContentView(int layoutResID) {        getWindow().setContentView(layoutResID);        initWindowDecorActionBar();    }    public void setContentView(View view) {        getWindow().setContentView(view);        initWindowDecorActionBar();    }    public void setContentView(View view, ViewGroup.LayoutParams params) {        getWindow().setContentView(view, params);        initWindowDecorActionBar();    }

getwindow返回一个PhoneWindow的对象,该对象关系如下图

这里写图片描述

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

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

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

从上面的源代码我们可以知道都是调用phoneWindow(getWindow函数返回)中的setContentView函数来初始化我们的View,接着我们看phoneWindow类的setContentView源码
setContentView(int layoutResID

 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) {          //初始化DecorView            installDecor();        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {//默认false,            mContentParent.removeAllViews();            /*mContentParent是什么呢,我们知道DecorView是一个FrameLayout,它是我们的根布局,除此之外我们都知道我们在写android app的时候会设置主题,而这个主题其实是一个布局文件(ViewGroup),这个布局文件中一班都有一个id为content的FramLayout的ViewGroup,mContentParent就是指着它           */        }        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,                    getContext());            transitionTo(newScene);        } else {        //将资源文件转换为View树树            mLayoutInflater.inflate(layoutResID, mContentParent);            //mLayoutInflater是一个LayoutInflater        }        final Callback cb = getCallback();        if (cb != null && !isDestroyed()) {            cb.onContentChanged();        }    }

setContentView(View view)

 public void setContentView(View view) {        setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));    }

可以看见setContentView(View view)调用setContentView(View view, ViewGroup.LayoutParams params)

setContentView(View view, ViewGroup.LayoutParams params)

 public void setContentView(View view, ViewGroup.LayoutParams params) {        // 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)) {            view.setLayoutParams(params);            final Scene newScene = new Scene(mContentParent, view);            transitionTo(newScene);        } else {        //直接根据位置参数添加View            mContentParent.addView(view, params);        }        final Callback cb = getCallback();        if (cb != null && !isDestroyed()) {            cb.onContentChanged();        }    }

可以看见和setContentView(int layoutResID)类似,只不过不需要xml,而是直接把view,add进去而已

setContentView中的installDecor()

既然知道初始化DecorView之后setContentView把资源添加进DecorView,那么问题的核心来了,第一次调用setContentView时时怎么初始化DecorView对象的呢?

 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);            //......            //初始化一堆属性值        }    }

从上面代码我们可以看出来installDecor干了两件事generateDecor初始化mDecor ,generateLayout(mDecor)初始化mContentParent 。我们一个一个来看
generateDecor

 protected DecorView generateDecor() {        return new DecorView(getContext(), -1);    }

没什么好说的,单纯new了一个DecorView
generateLayout(mDecor)

protected ViewGroup generateLayout(DecorView decor) {        // Apply data from current theme.//获得xml设置的样式主题        TypedArray a = getWindowStyle();        //......        //依据主题style设置一堆值进行设置        // Inflate the window decor.        int layoutResource;        //获得在代码中设置的样式主题        int features = getLocalFeatures();        //......        //根据设定好的features值选择不同的窗口修饰布局文件,得到layoutResource值        //把选中的窗口修饰布局文件添加到DecorView对象里,并且指定contentParent值        View in = mLayoutInflater.inflate(layoutResource, null);        //这个in中包含了我们我们选中的主题样式,然后帮in添加到decor中(DecorView)        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));        mContentRoot = (ViewGroup) in;        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);        //这里的findViewById(ID_ANDROID_CONTENT)其实就是就是主题中id为content的FramLayout        if (contentParent == null) {            throw new RuntimeException("Window couldn't find content container view");        }        //......        //继续一堆属性设置,完事返回contentParent        return contentParent;    }

所以installDecor方法实质就是产生mDecor和mContentParent对象。

Tips:
在这里顺带提一下:还记得我们平时写应用Activity时设置的theme或者feature吗(全屏啥的,NoTitle等)?我们一般是不是通过XML的android:theme属性或者java的requestFeature()方法来设置的呢?譬如:

通过java文件设置:requestWindowFeature(Window.FEATURE_NO_TITLE);通过xml文件设置:android:theme="@android:style/Theme.NoTitleBar"

其实我们平时requestWindowFeature()设置的值就是在这里通过getLocalFeature()获取的;而android:theme属性也是通过这里的getWindowStyle()获取的。

setContentView中的onContentChanged()

再回到setContentView中最后的代码部分,可以看见调用了Callback 的onContentChanged函数

 final Callback cb = getCallback();        if (cb != null && !isDestroyed()) {            cb.onContentChanged();        }

看见没有,首先通过getCallback获取对象cb(回调接口),PhoneWindow没有重写Window的这个方法,所以到抽象类Window中可以看到:

public final Callback getCallback() {        return mCallback;    }

这个mCallback在哪赋值的呢,继续看Window类发现有一个方法,如下:

blic void setCallback(Callback callback) {        mCallback = callback;    }

Window中的mCallback是通过这个方法赋值的,那就回想一下,Window又是Activity的组合成员,那就是Activity一定调运这个方法了,回到Activity发现在Activity的attach方法中进行了设置,如下

final void attach(Context context, ActivityThread aThread,        ......        mWindow.setCallback(this);        ......    }

也就是说Activity类实现了Window的Callback接口。那就是看下Activity实现的onContentChanged方法。如下:

public void onContentChanged() {    }

咦?onContentChanged是个空方法。那就说明当Activity的布局改动时,即setContentView()或者addContentView()方法执行完毕时就会调用该方法。所以当我们写App时,Activity的各种View的findViewById()方法等都可以放到该方法中,系统会帮忙回调。

0 0
原创粉丝点击