博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Activity的setContentView的流程
阅读量:6075 次
发布时间:2019-06-20

本文共 14957 字,大约阅读时间需要 49 分钟。

最简单的Activity中的内容大致是这样的:

public class MainActivity extends Activity {          @Override      public void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.main_activity);      }    }

setContentView

一般来说我们设置页面的内容视图是都是通过setContentView方法,那么我们就以2.3源码为例就来看看Activity中的setContentView到底做了什么吧。

1 /**  2  * Set the activity content from a layout resource.  The resource will be  3  * inflated, adding all top-level views to the activity.  4  *   5  * @param layoutResID Resource ID to be inflated.  6  */   7 public void setContentView(int layoutResID) {   8     getWindow().setContentView(layoutResID);   9 }  10    11 public Window getWindow() {  12     return mWindow;  13 }  14   15   16 private Window mWindow;

我们可以看到,实际上调用的mWindow的setContentView方法,在这篇文章中我们已经指出Window的实现类为PhoneWindow类

1 @Override   2 public void setContentView(int layoutResID) {   3     if (mContentParent == null) {   4         installDecor();         // 1、生成DecorView   5     } else {   6         mContentParent.removeAllViews();   7     }   8     mLayoutInflater.inflate(layoutResID, mContentParent);// 2、将layoutResId的布局添加到mContentParent中   9     final Callback cb = getCallback();  10     if (cb != null) {  11         cb.onContentChanged();  12     }  13 }  14     // 构建mDecor对象,并且初始化标题栏和Content Parent(我们要显示的内容区域)  15     private void installDecor() {  16     if (mDecor == null) {  17         mDecor = generateDecor();          // 3、构建DecorView  18         mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);  19         mDecor.setIsRootNamespace(true);  20     }  21     if (mContentParent == null) {  22         mContentParent = generateLayout(mDecor);              // 4、获取ContentView容器,即显示内容的区域  23   24         mTitleView = (TextView)findViewById(com.android.internal.R.id.title); 5、设置Title等  25         if (mTitleView != null) {  26             if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {  27                 View titleContainer = findViewById(com.android.internal.R.id.title_container);  28                 if (titleContainer != null) {  29                     titleContainer.setVisibility(View.GONE);  30                 } else {  31                     mTitleView.setVisibility(View.GONE);  32                 }  33                 if (mContentParent instanceof FrameLayout) {  34                     ((FrameLayout)mContentParent).setForeground(null);  35                 }  36             } else {  37                 mTitleView.setText(mTitle);  38             }  39         }  40     }  41 }  42   43     protected DecorView generateDecor() {  44     return new DecorView(getContext(), -1);    // 构建mDecor对象  45 }

我们可以看到,setContentView的基本流程简单概括就是如下几步:

 

1、构建mDecor对象。mDecor就是整个窗口的顶层视图,它主要包含了Title和Content View两个区域 (参考图1中的两个区域 ),Title区域就是我们的标题栏,Content View区域就是显示我们xml布局内容中的区域。关于mDecor对象更多说明也请参考这篇文章;

2、设置一些关于窗口的属性,初始化标题栏区域和内容显示区域;

这里比较复杂的就是generateLayout(mDecor)这个函数,我们一起来分析一下吧。

1 // 返回用于显示我们设置的页面内容的ViewGroup容器    2 protected ViewGroup generateLayout(DecorView decor) {    3    // Apply data from current theme.    4    // 1、获取窗口的Style属性    5    TypedArray a = getWindowStyle();    6     7    if (false) {    8        System.out.println("From style:");    9        String s = "Attrs:";   10        for (int i = 0; i < com.android.internal.R.styleable.Window.length; i++) {   11            s = s + " " + Integer.toHexString(com.android.internal.R.styleable.Window[i]) + "="   12                    + a.getString(i);   13        }   14        System.out.println(s);   15    }   16    // 窗口是否是浮动的   17    mIsFloating = a.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, false);   18    int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)   19            & (~getForcedWindowFlags());   20    if (mIsFloating) {   21        setLayout(WRAP_CONTENT, WRAP_CONTENT);   22        setFlags(0, flagsToUpdate);   23    } else {   24        setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);   25    }   26    // 设置是否不显示title区域   27    if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) {   28        requestFeature(FEATURE_NO_TITLE);   29    }   30    // 设置全屏的flag   31    if (a.getBoolean(com.android.internal.R.styleable.Window_windowFullscreen, false)) {   32        setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN&(~getForcedWindowFlags()));   33    }   34    35    if (a.getBoolean(com.android.internal.R.styleable.Window_windowShowWallpaper, false)) {   36        setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));   37    }   38    39    WindowManager.LayoutParams params = getAttributes();   40    // 设置输入法模式   41    if (!hasSoftInputMode()) {   42        params.softInputMode = a.getInt(   43                com.android.internal.R.styleable.Window_windowSoftInputMode,   44                params.softInputMode);   45    }   46    47    if (a.getBoolean(com.android.internal.R.styleable.Window_backgroundDimEnabled,   48            mIsFloating)) {   49        /* All dialogs should have the window dimmed */   50        if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) {   51            params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;   52        }   53        params.dimAmount = a.getFloat(   54                android.R.styleable.Window_backgroundDimAmount, 0.5f);   55    }   56    // 窗口动画   57    if (params.windowAnimations == 0) {   58        params.windowAnimations = a.getResourceId(   59                com.android.internal.R.styleable.Window_windowAnimationStyle, 0);   60    }   61    62    // The rest are only done if this window is not embedded; otherwise,   63    // the values are inherited from our container.   64    if (getContainer() == null) {   65        if (mBackgroundDrawable == null) {   66            if (mBackgroundResource == 0) {   67                mBackgroundResource = a.getResourceId(   68                        com.android.internal.R.styleable.Window_windowBackground, 0);   69            }   70            if (mFrameResource == 0) {   71                mFrameResource = a.getResourceId(com.android.internal.R.styleable.Window_windowFrame, 0);   72            }   73            if (false) {   74                System.out.println("Background: "   75                        + Integer.toHexString(mBackgroundResource) + " Frame: "   76                        + Integer.toHexString(mFrameResource));   77            }   78        }   79        mTextColor = a.getColor(com.android.internal.R.styleable.Window_textColor, 0xFF000000);   80    }   81    82    // Inflate the window decor.    83    // 2、根据一些属性来选择不同的顶层视图布局,例如设置了FEATURE_NO_TITLE的属性,那么就选择没有Title区域的那么布局;   84    // layoutResource布局就是整个Activity的布局,其中含有title区域和content区域,content区域就是用来显示我通过   85    // setContentView设置进来的内容区域,也就是我们要显示的视图。   86    87    int layoutResource;   88    int features = getLocalFeatures();   89    // System.out.println("Features: 0x" + Integer.toHexString(features));   90    if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {   91        if (mIsFloating) {   92            layoutResource = com.android.internal.R.layout.dialog_title_icons;   93        } else {   94            layoutResource = com.android.internal.R.layout.screen_title_icons;   95        }   96        // System.out.println("Title Icons!");   97    } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0) {   98        // Special case for a window with only a progress bar (and title).   99        // XXX Need to have a no-title version of embedded windows.  100        layoutResource = com.android.internal.R.layout.screen_progress;  101        // System.out.println("Progress!");  102    } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {  103        // Special case for a window with a custom title.  104        // If the window is floating, we need a dialog layout  105        if (mIsFloating) {  106            layoutResource = com.android.internal.R.layout.dialog_custom_title;  107        } else {  108            layoutResource = com.android.internal.R.layout.screen_custom_title;  109        }  110    } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {  111        // If no other features and not embedded, only need a title.  112        // If the window is floating, we need a dialog layout  113        if (mIsFloating) {  114            layoutResource = com.android.internal.R.layout.dialog_title;  115        } else {  116            layoutResource = com.android.internal.R.layout.screen_title;  117        }  118        // System.out.println("Title!");  119    } else {  120        // Embedded, so no decoration is needed.  121        layoutResource = com.android.internal.R.layout.screen_simple;  122        // System.out.println("Simple!");  123    }  124   125    mDecor.startChanging();  126    // 3、加载视图  127    View in = mLayoutInflater.inflate(layoutResource, null);  128    // 4、将layoutResource的内容添加到mDecor中  129    decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));  130    // 5、获取到我们的内容显示区域,这是一个ViewGroup类型的,其实是FrameLayout  131    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);  132    if (contentParent == null) {  133        throw new RuntimeException("Window couldn't find content container view");  134    }  135   136    if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {  137        ProgressBar progress = getCircularProgressBar(false);  138        if (progress != null) {  139            progress.setIndeterminate(true);  140        }  141    }  142   143    // 6、设置一些背景、title等属性  144    // Remaining setup -- of background and title -- that only applies  145    // to top-level windows.  146    if (getContainer() == null) {  147        Drawable drawable = mBackgroundDrawable;  148        if (mBackgroundResource != 0) {  149            drawable = getContext().getResources().getDrawable(mBackgroundResource);  150        }  151        mDecor.setWindowBackground(drawable);  152        drawable = null;  153        if (mFrameResource != 0) {  154            drawable = getContext().getResources().getDrawable(mFrameResource);  155        }  156        mDecor.setWindowFrame(drawable);  157   158        // System.out.println("Text=" + Integer.toHexString(mTextColor) +  159        // " Sel=" + Integer.toHexString(mTextSelectedColor) +  160        // " Title=" + Integer.toHexString(mTitleColor));  161   162        if (mTitleColor == 0) {  163            mTitleColor = mTextColor;  164        }  165   166        if (mTitle != null) {  167            setTitle(mTitle);  168        }  169        setTitleColor(mTitleColor);  170    }  171   172    mDecor.finishChanging();  173   174    return contentParent;

其实也就是这么几个步骤:

 

1、获取用户设置的一些属性与Flag;

2、根据一些属性选择不同的顶层视图布局,例如FEATURE_NO_TITLE则选择没有title的布局文件等;这里我们看一个与图1中符合的顶层布局吧,即layoutResource = c

1 
2 3
6
7
12
13
17
24
25
26
32

我们可以看到有两个区域,即title区域和content区域,generateLayout函数中的

// 5、获取到我们的内容显示区域,这是一个ViewGroup类型的,其实是FrameLayout  ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

获取的就是xml中id为content的FrameLayout,这个content就是我们的内容显示区域。整个布局对应的效果如下 :

这两个区域就组成了mDecor视图,我们的main_activity.xml就是放在内容视图这个区域的。

3、加载顶层布局文件,转换为View,将其添加到mDecor中;

4、获取内容容器Content Parent,即用于显示我们的内容的区域;

5、设置一些背景图和title等。

在经过这几步,我们就得到了mContentParent,这就是用来装载我们的视图的ViewGroup。再回过头来看setContentView函数:

1 public void setContentView(int layoutResID) {   2     if (mContentParent == null) {   3         installDecor();         // 1、生成DecorView,并且根据窗口属性加载顶级视图布局、获取mContentParent、设置一些基本属性等   4     } else {   5         mContentParent.removeAllViews();   6     }   7     mLayoutInflater.inflate(layoutResID, mContentParent);// 2、将layoutResId加载到mContentParent中,这里的layoutResId就是我们的main_activity.xml   8     final Callback cb = getCallback();   9     if (cb != null) {  10         cb.onContentChanged();  11     }  12 }

我们看看LayoutInflater的inflate函数吧 : 

1 /**  2  * Inflate a new view hierarchy from the specified xml resource. Throws  3  * {@link InflateException} if there is an error.  4  *   5  * @param resource ID for an XML layout resource to load (e.g.,  6  *        R.layout.main_page)  7  * @param root Optional view to be the parent of the generated hierarchy.  8  * @return The root View of the inflated hierarchy. If root was supplied,  9  *         this is the root View; otherwise it is the root of the inflated 10  *         XML file. 11  */  12 public View inflate(int resource, ViewGroup root) {  13     return inflate(resource, root, root != null);  14 }  15   16 /** 17  * Inflate a new view hierarchy from the specified xml resource. Throws 18  * {@link InflateException} if there is an error. 19  *  20  * @param resource ID for an XML layout resource to load (e.g., 21  *        R.layout.main_page) 22  * @param root Optional view to be the parent of the generated hierarchy (if 23  *        attachToRoot is true), or else simply an object that 24  *        provides a set of LayoutParams values for root of the returned 25  *        hierarchy (if attachToRoot is false.) 26  * @param attachToRoot Whether the inflated hierarchy should be attached to 27  *        the root parameter? If false, root is only used to create the 28  *        correct subclass of LayoutParams for the root view in the XML. 29  * @return The root View of the inflated hierarchy. If root was supplied and 30  *         attachToRoot is true, this is root; otherwise it is the root of 31  *         the inflated XML file. 32  */  33 public View inflate(int resource, ViewGroup root, boolean attachToRoot) {  34     if (DEBUG) System.out.println("INFLATING from resource: " + resource);  35     XmlResourceParser parser = getContext().getResources().getLayout(resource);  36     try {  37         return inflate(parser, root, attachToRoot);  38     } finally {  39         parser.close();  40     }  41 }

实际上就是将layoutResId这个布局的视图附加到mContentParent中。

DecorView

移步 :  。

ViewGroup

ViewGroup从语义上来说就是视图组,它也继承自View类,它其实就是视图的容器。我们看官方的定义 : 

* A ViewGroup is a special view that can contain other views  * (called children.) The view group is the base class for layouts and views  * containers. This class also defines the  * {@link android.view.ViewGroup.LayoutParams} class which serves as the base  * class for layouts parameters.

我们通过ViewGroup来组织、管理子视图,例如我们常见的FrameLayout、LinearLayout、RelativeLayout、ListView等都是ViewGroup类型,总之只要能包含其他View或者ViewGroup的都是ViewGroup类型。使用ViewGroup来构建视图树。

 

 

View

View就是UI界面上的一个可见的组件,任何在UI上可见的都为View的子类。我们看官方定义 :

 

TextView、Button、ImageView、FrameLayout、LinearLayout、ListView等都是View的子类。

这样,ViewGroup类型的视图管理嵌套在里面的ViewGroup以及View控件组成了丰富多彩的用户界面。例如我们开篇的Hello World的视图结构是这样的 :

总结 

整个窗口由Title区域和Content区域组成,Content区域就是我们要显示内容的区域,在这个区域中mContentParent是根ViewGroup,由mContentParent组织、管理其子视图,从而构建整个视图树。当Activity启动时,就将这些内容就会显示在手机上。

你可能感兴趣的文章
Eclipse遇到Initializing Java Tooling解决办法
查看>>
while((ch = getchar()) != '\n')
查看>>
好程序员web前端分享JS检查浏览器类型和版本
查看>>
Oracle DG 逻辑Standby数据同步性能优化
查看>>
exchange 2010 队列删除
查看>>
「翻译」逐步替换Sass
查看>>
H5实现全屏与F11全屏
查看>>
处理excel表的列
查看>>
C#数据采集类
查看>>
quicksort
查看>>
【BZOJ2019】nim
查看>>
四部曲
查看>>
LINUX内核调试过程
查看>>
【HDOJ】3553 Just a String
查看>>
Java 集合深入理解(7):ArrayList
查看>>
2019年春季学期第四周作业
查看>>
linux环境配置
查看>>
ASP.NET MVC中从前台页面视图(View)传递数据到后台控制器(Controller)方式
查看>>
lintcode:next permutation下一个排列
查看>>
一个想法(续二):换个角度思考如何解决IT企业招聘难的问题!
查看>>