码迷,mamicode.com
首页 > 其他好文 > 详细

setContentView()入门

时间:2016-05-07 10:32:21      阅读:229      评论:0      收藏:0      [点我收藏+]

标签:

基础

        整个过程基本上都在PhoneWindow中完成的。先罗列里面会用到的方法。如下:

    //可以看出它mDecor为DecorView对象
    protected DecorView generateDecor() {
        return new DecorView(getContext(), -1);
    }
	
    //findViewById()如下:
    public View findViewById(@IdRes int id) {
        return getDecorView().findViewById(id);
    }
	//获取theme中关于window属性的设置
    public final TypedArray getWindowStyle() {
        synchronized (this) {
            if (mWindowStyle == null) {
                mWindowStyle = mContext.obtainStyledAttributes(
                        com.android.internal.R.styleable.Window);
            }
            return mWindowStyle;
        }
    }
    public final TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) {
        return getTheme().obtainStyledAttributes(attrs);
    }
	//是否有指定的feature。
    public boolean hasFeature(int feature) {
        return (getFeatures() & (1 << feature)) != 0;
    }
	//设置feature。上面的getFeatures()只是返回mFeatures
    public boolean requestFeature(int featureId) {
        final int flag = 1<<featureId;
        mFeatures |= flag;
        mLocalFeatures |= mContainer != null ? (flag&~mContainer.mFeatures) : flag;
        return (mFeatures&flag) != 0;
    }

PhoneWindow#setContentView

        调用Activity#setContentView()最终会调用到PhoneWindow#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);
        }
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }
  首先看installDecor的作用:创建一个DecorView对象,并赋值给mDecor。同时为mContentParent进行赋值。
        其次,如果没设置FEATURE_CONTENT_TRANSITIONS,那就移除所有的原来组件,并且调用mLayoutInflater.inflate(layoutResID, mContentParent);将新布局添加到mContentParent中。
如果有FEATURE_CONTENT_TRANSITIONS,会通过transitionTo进行界面切换,切换过程会有一个淡入淡出的效果。

        最后会调用onContentChanged()回调。

        整体逻辑就这么多,关键是要看mContentParent指的是什么——因为它是我们写的布局的父类。

PhoneWindow#generateLayout(DecorView)

        因为在installDecor()中会调用generateLayout(mDecor)生成mContentParent,所以先看generateLayout(mDecor)方法。
        其方法看着比较多,但实质就是根据该activity的theme找到对应的布局,inflate该布局并返回id为android:id/content的ViewGroup。

     protected ViewGroup generateLayout(DecorView decor) {
        TypedArray a = getWindowStyle();// Apply data from current theme.
        //一个输出语句,省略
		//获取theme中的windowIsFloating属性
        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);
        }
		
        //根据指定的属性调用requestFeatures()设置一些feature和flags,具体代码略
        if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
            requestFeature(FEATURE_NO_TITLE);//无标题feature
        } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
            requestFeature(FEATURE_ACTION_BAR);// Don't allow an action bar if there is no title.
        }
	//略了一部分代码
        if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) {
            requestFeature(FEATURE_SWIPE_TO_DISMISS);//侧滑返回。但很可惜api21以后才能用,而且还有bug
        }
       //略了一部分setFlags与requestFeature代码,它们都是根据a中设置的一些属性值
        int layoutResource;
        int features = getLocalFeatures();
        //根据features值获取layoutResourece的值,它代表一个布局的id。代码略
        mDecor.startChanging();
	//添加布局,并将布局添加到decor上。
        View in = mLayoutInflater.inflate(layoutResource, null);
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        mContentRoot = (ViewGroup) in;
	//获取添加自己View的viewgroup,layoutResource会有好几个,但都会含义一个id为@android:id/content的ViewGroup,例如可以见下面的screen_swipe_dismiss.xml
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        //一些关于features进行的设置
        mDecor.finishChanging();

        return contentParent;
    }
        具体的解释上注释中。其中ID_ANDROID_CONTRENT定义如下:
     /**
     * The ID that the main layout in the XML layout file should have.
     */
    public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;

        从这个注释中我们还可以看出contentParent的作用。

installDecor()

再分析下installDecor()代码:
    private void installDecor() {
        if (mDecor == null) {
			//生成mDecor,并进行一系列的设置
            mDecor = generateDecor();
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);//子View不获取焦点时,它才获取焦点
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
            // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
            mDecor.makeOptionalFitsSystemWindows();
            //mDecor是一个ViewGroup,这里省略的就是对其中的某些子View进行初始化设置的代码
			
            if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
                mDecor.setBackgroundFallback(mBackgroundFallbackResource);
            }


            // Only inflate or create a new TransitionManager if the caller hasn't
            // already set a custom one.
            //一些关于TransitionManager的初始化工作
        }
    }
  主要过程分为两部分:生成mDecor。

        根据mDecor,通过generateLayout(mDecor)生成mContentParent。然后对mContentParent进行一系列设置。

总结

上面就是整个过程的分析:

        先初始化DecorView,然后根据设置的features不同为decorView加载不同的布局。
接着在decorView中find一个id为android:id/content的mContentParent——这即是自己布局的父容器。
最后通过LayoutInflater.inflate将自己的布局添加到mContentParent中。 

文件

        一个decorview加载的布局:

	<com.android.internal.widget.SwipeDismissLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/content"
    android:fitsSystemWindows="true"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    />
        从中可以看出,它的确有一个id为android:id/content的ViewGroup。

setContentView()入门

标签:

原文地址:http://blog.csdn.net/u010410408/article/details/51331159

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!