Warning: file_put_contents(/datas/wwwroot/jiajiahui/core/caches/caches_template/2/default/show.php): failed to open stream: Permission denied in /datas/wwwroot/jiajiahui/core/libraries/classes/template_cache.class.php on line 55

Warning: chmod(): Operation not permitted in /datas/wwwroot/jiajiahui/core/libraries/classes/template_cache.class.php on line 56
android setContentView源码分析 - 源码之家

android setContentView源码分析

         Activity是android的四大组件之一,其重要性不言而喻,而且在我们的开发过程中打交道最多的也是它。在设置视图的时候,我们一般都是通过setContentView来加载我们的布局资源的,看起来很简单的一行代码setContentView(),但是实际上里面都做了哪些事情你真的知道吗?

         android setContentView源码分析

在开始讲解setContentView的源码之前,你首先要弄懂上面的这张图,弄不懂也没关系,但是你需要记住它,这样有利于后面的源码理解。

源码解析

@Override
public void setContentView(int layoutResID) {
        // mContentParent即DecorView
        // mContentParent是否为空,第一次进来的时候为空
        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;
}
private void installDecor() {
        mForceDecorInstall = false;
        // mDecor是否为空,第一次进来的时候为空
        if (mDecor == null) {
            // 初始化DecorView
            mDecor = generateDecor(-1);
            // 省略部分代码
        } else {
            mDecor.setWindow(this);
        }
        // mContentParent是否为空,第一次进来的时候为空
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
            // 省略部分代码
        }
}
// 初始化DecorView
protected DecorView generateDecor(int featureId) {
    // 省略部分代码
    // 创建DecorView
    return new DecorView(context, featureId, this, getAttributes());
}
protected ViewGroup generateLayout(DecorView decor) {
     TypedArray a = getWindowStyle();
 
    // 设置 当前Activity配置的主题theme
	// 窗口是否是浮动的
	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);
	}
	// 窗口中ActionBar是否在布局空间内
	if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) {
		requestFeature(FEATURE_ACTION_BAR_OVERLAY);
	}
 
	if (a.getBoolean(R.styleable.Window_windowActionModeOverlay, false)) {
		requestFeature(FEATURE_ACTION_MODE_OVERLAY);
	}
 
	if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) {
		requestFeature(FEATURE_SWIPE_TO_DISMISS);
	}
	// 窗口是否全屏显示
	if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {
		setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
	}
 
	... ...
   
	//窗口状态栏颜色配置
	if (!mForcedStatusBarColor) {
		mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000);
	}
	//窗口导航栏颜色配置
	if (!mForcedNavigationBarColor) {
		mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000);
	}
 
	if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion
			>= android.os.Build.VERSION_CODES.HONEYCOMB) {
		if (a.getBoolean(
				R.styleable.Window_windowCloseOnTouchOutside,false)) {
			setCloseOnTouchOutsideIfNotSet(true);
		}
	}
 
	WindowManager.LayoutParams params = getAttributes();
	//输入法配置
	if (!hasSoftInputMode()) {
		params.softInputMode = a.getInt(
				R.styleable.Window_windowSoftInputMode,
				params.softInputMode);
	}
 
    if (a.getBoolean(R.styleable.Window_backgroundDimEnabled, mIsFloating)) {
            /* All dialogs should have the window dimmed */
            if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) {
                params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
            }
            if (!haveDimAmount()) {
                params.dimAmount = a.getFloat(
                        android.R.styleable.Window_backgroundDimAmount, 0.5f);
            }
        }
        //设置当前Activity的出现动画效果
        if (params.windowAnimations == 0) {
            params.windowAnimations = a.getResourceId(
                    R.styleable.Window_windowAnimationStyle, 0);
        }
 
        // The rest are only done if this window is not embedded; otherwise,
        // the values are inherited from our container.
        if (getContainer() == null) {
            if (mBackgroundDrawable == null) {
                if (mBackgroundResource == 0) {
                    mBackgroundResource = a.getResourceId(
                            R.styleable.Window_windowBackground, 0);
                }
                if (mFrameResource == 0) {
                    mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0);
                }
                mBackgroundFallbackResource = a.getResourceId(
                        R.styleable.Window_windowBackgroundFallback, 0);
                if (false) {
                    System.out.println("Background: "
                            + Integer.toHexString(mBackgroundResource) + " Frame: "
                            + Integer.toHexString(mFrameResource));
                }
            }
            mElevation = a.getDimension(R.styleable.Window_windowElevation, 0);
            mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false);
            mTextColor = a.getColor(R.styleable.Window_textColor, Color.TRANSPARENT);
        }
	}
 
	// 为窗口添加 decor根布局
    // Inflate the window decor.
 
    int layoutResource;
    int features = getLocalFeatures();
    if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
        if (mIsFloating) {
            TypedValue res = new TypedValue();
            getContext().getTheme().resolveAttribute(
                    com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true);
            layoutResource = res.resourceId;
        } else {
            layoutResource = com.android.internal.R.layout.screen_title_icons;
        }
        // XXX Remove this once action bar supports these features.
        removeFeature(FEATURE_ACTION_BAR);
        // System.out.println("Title Icons!");
    } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
        && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
        // Special case for a window with only a progress bar (and title).
        // XXX Need to have a no-title version of embedded windows.
        layoutResource = com.android.internal.R.layout.screen_progress;
        // System.out.println("Progress!");
    } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
        // Special case for a window with a custom title.
        // If the window is floating, we need a dialog layout
        if (mIsFloating) {
            TypedValue res = new TypedValue();
            getContext().getTheme().resolveAttribute(
                    com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true);
            layoutResource = res.resourceId;
        } else {
            layoutResource = com.android.internal.R.layout.screen_custom_title;
        }
        // XXX Remove this once action bar supports these features.
        removeFeature(FEATURE_ACTION_BAR);
    } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
         // If no other features and not embedded, only need a title.
         // If the window is floating, we need a dialog layout
         if (mIsFloating) {
            TypedValue res = new TypedValue();
            getContext().getTheme().resolveAttribute(
                    com.android.internal.R.attr.dialogTitleDecorLayout, res, true);
            layoutResource = res.resourceId;
         } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
             if ((features & (1 << FEATURE_ACTION_BAR_OVERLAY)) != 0) {
                layoutResource = com.android.internal.R.layout.screen_action_bar_overlay;
             } else {
                layoutResource = com.android.internal.R.layout.screen_action_bar;
             }
         } else {
            layoutResource = com.android.internal.R.layout.screen_title;
         }
         // System.out.println("Title!");
         } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
             layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode;
         } else {
             layoutResource = com.android.internal.R.layout.screen_simple;
         }
 
         mDecor.startChanging();
 
		 // 通过布局填充器LayoutInflater将layoutResource填充为布局,加载到decor上
         View in = mLayoutInflater.inflate(layoutResource, null);
         decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
 
		 // 找到ID为com.android.internal.R.id.content的ViewGroup布局并进行返回
         ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
         if (contentParent == null) {
             throw new RuntimeException("Window couldn't find content container view");
         }
 
         if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
            ProgressBar progress = getCircularProgressBar(false);
            if (progress != null) {
                progress.setIndeterminate(true);
            }
        }
 
    // Remaining setup -- of background and title -- that only applies
    // to top-level windows.
	// 设置窗口的背景标题等
    if (getContainer() == null) {
            Drawable drawable = mBackgroundDrawable;
        if (mBackgroundResource != 0) {
            drawable = getContext().getResources().getDrawable(mBackgroundResource);
        }
        mDecor.setWindowBackground(drawable);
        drawable = null;
        if (mFrameResource != 0) {
            drawable = getContext().getResources().getDrawable(mFrameResource);
        }
        mDecor.setWindowFrame(drawable);
 
        if (mTitleColor == 0) {
            mTitleColor = mTextColor;
        }
 
        if (mTitle != null) {
            setTitle(mTitle);
        }
        setTitleColor(mTitleColor);
    }
 
    mDecor.finishChanging();
    return contentParent;
}

在这里先总结一下:

1.我们知道在Activity中设置setContentView()首先会去执行到Activity的setContentView()方法

2.而Activity的setContentView会去执行到Window类里面,而Window里面的setContentView()方法是个抽象方法,最终会执行到Window的具体类PhoneWindow里面的setContentView()方法里面

3.在PhoneWinow里面首先会去判断mContentParent是否为空,那么mContentParent是什么呢?其实mContentParent是DecorView类的根布局,而DecorView是PhoneWindow的一个内部类

4.当mContentParent为空的时候,会去实例化DecorView,同时在实例化DecorView的时候会去设置状态栏的一些属性并且找到一个ID为com.android.internal.R.id.content的ViewGroup布局并进行返回

5.最终执行到setContentView()方法的第16行mLayoutInflater.inflate(layoutResID, mContentParent);其中layoutResID为我们传递进去的布局文件,mContentParent为ID为com.android.internal.R.id.content的ViewGroup

 

那么mLayoutInflater.inflate(layoutResID, mContentParent);最终会执行到哪里呢?

查看源码知道,最终会执行到LayoutInflater的inflate方法里面

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();
        if (DEBUG) {
            Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
                    + Integer.toHexString(resource) + ")");
        }
        // XmlResourceParser是专门用来解析XML文件的
        final XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
}
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
    synchronized (mConstructorArgs) {
            // 省略部分代码
            
            // attrs是传入的布局layout在xml文件里面设置的属性集合
            final AttributeSet attrs = Xml.asAttributeSet(parser);
            
            // 将ViewGroup类型的参数root赋值给result
            View result = root;

            // temp是传入的参数resource的根布局View
           final View temp = createViewFromTag(root, name, inflaterContext, attrs);
 
 
           ViewGroup.LayoutParams params = null;
 
 
           // 实例化temp视图内的所有子视图
           rInflateChildren(parser, temp, attrs, true);
 
 
           if (root != null) {
              // 根据attrs属性集,创建布局参数params
              params = root.generateLayoutParams(attrs);
	          // 如果temp不需要添加到root中,那么为temp设置布局参数params
              if (!attachToRoot) {
                  temp.setLayoutParams(params);
                }
            }
 
 
            if (root != null && attachToRoot) {
                 // 将temp添加到root中,并使用布局参数params
                 root.addView(temp, params);
            }
 
 
            if (root == null || !attachToRoot) {
                  // 将temp赋值给result(在此之前,result==root)
                  result = temp;
            }
            return result;
    }
}

到这里setContentView就分析完了,现在我们回过头来看看setContentView()方法到底做了什么?

其实答案很简单,setContentView()方法只是加载了我们设置的布局文件并对里面的属性进行了解析,其他的事情什么都没做,那么我们设置的布局是如何显示在界面上的呢?

哈哈哈,相信大家都已经猜到了,最终的布局绘制是通过Activity的生命周期来实现的,那么下结我们就一起来分析一下View的绘制流程