Android-Activity中setContentView流程解析

Posted 山水相逢-z

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android-Activity中setContentView流程解析相关的知识,希望对你有一定的参考价值。

在Activity中加载布局的时候,我们都知道调用的是setContentView方法,那么具体是如何实现的呢?
本文基于参考其他人博客以及自己翻阅源码做一个记录。

随便找到一个Activity,点击setContentView方法可以看到实现:

public void setContentView(@LayoutRes int layoutResID) 
        getWindow().setContentView(layoutResID);//1
        initWindowDecorActionBar();//2
    

layoutResID是我们传入的布局id,所以我们先主要关注1处。可以看到这里调用了getWindow().setContentView,点进去getWindow看一下:

public Window getWindow() 
        return mWindow;
    

获取的是一个Window对象,找到Window类,它是一个抽象类,在类注释中我们可以看到下面一段:

	The only existing implementation of this abstract class is
 * android.view.PhoneWindow, which you should instantiate when needing a
 * Window.

可以看到,Window唯一的实现类就是PhoneWindow。再点进去PhoneWindowsetContentView方法:

public class PhoneWindow extends Window implements MenuBuilder.Callback 
    //...
    //...
    //...
    @Override
    public void setContentView(int layoutResID) 
        if (mContentParent == null) 
            installDecor();
         else 
            mContentParent.removeAllViews();
        
        mLayoutInflater.inflate(layoutResID, mContentParent);
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) 
            cb.onContentChanged();
        
    

    @Override
    public void setContentView(View view) 
        setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    
  
    @Override
    public void setContentView(View view, ViewGroup.LayoutParams params) 
        if (mContentParent == null) 
            installDecor();
         else 
            mContentParent.removeAllViews();
        
        mContentParent.addView(view, params);
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) 
            cb.onContentChanged();
        
    

    //...
    //...
    //...

这里我们关注setContentView(int layoutResID)实现。
mContentParent不为空时,会先移除其上所以子view,然后通过mLayoutInflater.inflate(layoutResID, mContentParent)将我们的布局文件加载上去,当mContentParent为空时,会先调用installDecor()方法,click it:

private void installDecor() 
            if (mDecor == null) 
                mDecor = generateDecor();
                //...
                
            
            if (mContentParent == null) 
                mContentParent = generateLayout(mDecor);

                        //...
                    
                
            
    

只看主流程代码,逻辑很清晰,mContentParent为空的时候,会执行generateLayout()方法,同时需要传入一个mDecor,那么mDecor是什么东西呢,我们可以点进generateDecor方法看一下:

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

嘿,又出来了一个DecorView,这是个什么东西呢?查看它的源码:

private final class DecorView extends FrameLayout 

可以看到,DecorView继承FrameLayout,是一个ViewGroup。回到前面的generateLayout(mDecor),看一下我们往DecorView里都有什么东西:

public class PhoneWindow extends Window implements MenuBuilder.Callback 

    protected ViewGroup generateLayout(DecorView decor) 
        // 此处省去一堆代码,设置窗口属性

        int layoutResource;
        // 此处省去一堆代码,根据不同的主题使用不同的布局资源

        // 这里才是重点,向 DecorView 添加布局,并且从 DecorView 中查找出 contentParent
        View in = mLayoutInflater.inflate(layoutResource, null);
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) 
            throw new RuntimeException("Window couldn't find content container view");
        

        // 此处省去一堆代码设置窗口背景和标题
        
        return contentParent;
    
    

在这个方法里,我们contentParent其实指向的就是ID_ANDROID_CONTENT,也就是R.id.content。到目前为止,我们可以用一张图来概括上边儿的流程:

找到mContentParent之后,回到setContentView方法中:

// 解析我们设置的布局资源并且设置 mContentParent 为父布局
        mLayoutInflater.inflate(layoutResID, mContentParent);

至此,我们自己的布局就加载在了Activity上,

以上是关于Android-Activity中setContentView流程解析的主要内容,如果未能解决你的问题,请参考以下文章

Android-Activity中setContentView流程解析

android-Activity(四大组件之一)

Android-Activity跳转时动画

Android-Activity启动流程

Android-Activity启动模式(launchMode)

Ueditor百度编辑器中的 setContent()方法的使用