setContentView()源码的理解

原创 2016年05月30日 21:05:47

setContentView()源码的理解

通俗的解释

使用场景

在Activity中的onCreate()方法中,我们会使用setContentView()给Activity加载布局。但实际情况却不是我们通常使用时理解的那样。正如setContentView()方法名那样,是将layout设置给content,而不是设置给Activity。

先放结论

Activity的布局是有特定的模板,我们通常只是给Content区域填充View
以screen_custom_title.xml模板为例(查看源码请按这里),会得到下图

这里写图片描述
简单梳理一下执行流程:

Created with Raphaël 2.1.0setContentView(layout)初始化DecorView(FrameLayout)根据情况初始化相应的ViewGroup将ViewGroup添加到DecorView中在ViewGroup中找到Content(FrameLayout)对象将Layout填充到Content中其他操作

以findViewById()为例,来进一步理解

在Activity中,我们使用findViewById()都很熟练,感觉没什么特别的。但是在Framework中,想要找到特定的控件对象,会使用

view.findViewById(id);

来获取相应的控件。实际上,findViewById()是View当中的方法在Activity中,findViewById()实际上是调用DecorView.findViewById(),只是不给使用者展示。也就是说,DecorView是外层的布局,也从侧面验证了上图。

RTFSC解释

为了把握整体思路,这里并没有详细地说明每一行代码

Activity中的相关代码

public void setContentView(@LayoutRes int layoutResID) {
    //会调用PhoneWindow中的代码
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

PhoneWindow中setContentView()相关代码

public void setContentView(int layoutResID) {
    //mContentParent,也就是Content(上图浅紫色,与Title平级的部分)
    if (mContentParent == null) {
        //DecorView会在这里初始化,ViewGroup也在该方法中添加到DecorView中了,同时找打了mContentParent对象
        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 {
        //将我们的Layout填充到mContentParent中
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
}

installDecor()中相关的代码,有省略代码

private void installDecor() {
    if (mDecor == null) {
        //初始化DecorView
        mDecor = generateDecor();
        //一些相关的配置
        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        mDecor.setIsRootNamespace(true);
        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
        }
    }
    if (mContentParent == null) {
        //将ViewGroup添加到DecorView中,并将Content找到,返回
        mContentParent = generateLayout(mDecor);

        mDecor.makeOptionalFitsSystemWindows();

        //自带装饰的Content
        //DecorContentParent名字起的也很生动,带装饰的
        final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
                R.id.decor_content_parent);

        if (decorContentParent != null) {
            //对decorContentParent的操作
            //设置logo,title,icon,options...
        } else {
            //对Title的一些操作
            //...
        }

        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.
        if (hasFeature(FEATURE_ACTIVITY_TRANSITIONS)) {
            //一些过渡的操作
            //...
        }
    }
}

generateLayout中相关的代码,有省略

protected ViewGroup generateLayout(DecorView decor) {
    //获取相关属性
    TypedArray a = getWindowStyle();

    if (false) {
        //估计是调试用的代码
        //...
    }

    //各种属性的设置
    //...

    //ViewGroup的Layout用的id
    int layoutResource;
    int features = getLocalFeatures();
    //根据设置来确定layoutResource是哪个layout
    //...

    //做个开始标记
    mDecor.startChanging();

    //填充ViewGroup
    View in = mLayoutInflater.inflate(layoutResource, null);
    //将ViewGroup添加到DecorView中
    decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    mContentRoot = (ViewGroup) in;

    //获取Content对象
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

    //一些操作
    //...

    //做个结束标记
    mDecor.finishChanging();

    //返回ViewGroup
    return contentParent;
}

结语

setContentView()相关的代码还是很多的,需要抓住重点,这样才不会影响整体的思维。
如果看完了还是感觉云里雾里,那就先把图片记住,剩下的以后再说

转载请标明出处:http://blog.csdn.net/qq_26411333/article/details/51541129

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Android源码分析-深入理解setContentView方法

一般我们都是这样使用setContentView: @Override protected void onCreate(Bundle savedInstanceState) { ...

android中布局和View创建的源码分析---setContentView

android中布局和View创建的源码分析因为我们使用的是拦截view创建的过程来实现插件换肤的功能,所以首先要熟悉android中创建视图的过程,下面让我们一起来分析下源码,这里我们从两个方面来分...

Android应用setContentView与LayoutInflater加载解析机制源码分析

【工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果】1 背景其实之所以要说这个话题有几个原因: 理解xml等控件是咋被显示的原理,通常大家写代码都...
  • yanbober
  • yanbober
  • 2015年05月26日 23:16
  • 37805

从源码分析Activity布局构成(setContentview)

古语有云:学而不思则罔。我们知道setContentView能够设置Activity的UI布局,但是你真正了解这个方法是干嘛的么?你真正了解Activity的UI布局结构么?这篇文章就为你揭开Acti...
  • huiblog
  • huiblog
  • 2016年11月19日 08:26
  • 117

Android setContentView 加载布局源码解析

转载请注明出处:1,背景作为Android 四大组件之一的Activity 在应用开发中在常见不过。 而回调Activity 生命周期的onCreat()以及加载布局的setContentView(...

View绘制之setContentView()源码分析

setContentView()Activity的setContentView()public void setContentView(View view, ViewGroup.LayoutParam...

从源码的角度说说Activity的setContentView的原理

我们在Activity开发的时候天天会用到这个方法,有时候还需要根据需求在setContentView调用的时候做一些动作,因此我们就需要知道它内部是如何工作的,我们来一起看一下: setConte...

SetContentView与LayoutInflater源码分析

setContentView与LayoutInflater源码分析

从源码的角度说说Activity的setContentView的原理(二)

前文http://blog.csdn.net/sahadev_/article/details/49072045虽然讲解了LayoutInflate的整个过程,但是其中很多地方是不准确不充分的,这一节...

源码分析setContentView加载布局文件的过程

我们都知道通过setContentView方法将xml布局文件加载到Activity中,然后屏幕才能显示出xml中定义的view。在整个过程中,无须做太多的操作,屏幕就能显示出正常的内容。现在我们从源...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:setContentView()源码的理解
举报原因:
原因补充:

(最多只允许输入30个字)