Android:从SetContentView()谈起

作者: 饱醉豚我去年买了个表 | 来源:发表于2016-09-09 17:43 被阅读245次

    当我们新建一个工程的时候,选择生成一个Activity,AS会自动给我们生成一个界面,那么这个界面是怎么生成的呢,下面我们就来分析一下:

    Activity中的代码:

    @Overrideprotected
     void onCreate(Bundle savedInstanceState) {    
              super.onCreate(savedInstanceState);    
              setContentView(R.layout.activity_main);}
    

    XML中的代码:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"   
     android:layout_width="match_parent"    
     android:layout_height="match_parent">  
      <EditText     
         android:layout_width="match_parent"     
         android:layout_height="match_parent"   
         android:layout_margin="15dp"     
         android:gravity="top"     
         android:hint="这是一个EditTextView"       
         android:inputType="textMultiLine"  
         android:maxLines="20"     
         android:minLines="6"     
         android:textSize="30sp" />
      </RelativeLayout>
    
    Screenshot_2016-09-09-17-38-48-872.png

    用Hierarchy viewer工具来查看一下结构图:
    ![@P8]6ZC7MJ~LJFG(DPBWA.png](http:https://img.haomeiwen.com/i587163/e85daee233003243.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

    DecorView以及它的子View一目了然,先看下SetContentView()的实现:

    @Override
    public void setContentView(int layoutResID) {    
     // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window   
     // decor, when theme attributes and the like are crystalized. Do not check the feature   
     // before this happens.    
    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();   
     }}
    
    // This is the top-level view of the window, containing the window decor.
    private DecorView mDecor;
    // This is the view in which the window contents are placed. It is either
    // mDecor itself, or a child of mDecor where the contents go.
    //mContentParent 是放置窗体内容的容器,也就是我们 setContentView()
    //时所加入的 View 视图树
    private ViewGroup mContentParent;
    

    DecorView是PhoneWindow的一个内部类,同时DecorView也是Activiy的顶级View,一般来说DecorView的内部包括导航栏(NavigationBar)和状态栏(StatusBar),但这个会随着主题的变化而发生改变。刚开始mContentParent的值是null,所以会走installDecor():

    if (mDecor == null) {   
       mDecor = generateDecor(); 
       mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); 
       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();  
       final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(R.id.decor_content_parent);
    
    protected DecorView generateDecor() {  
      return new DecorView(getContext(), -1);
    }
    

    在installDecor()里面通过调用generateDecor()方法来初始化DecorView,此时DecorView什么都没有,只是一个空的FrameLayout,往下走,来到generateLayout(mDecor),跳过一些主题布局设置,直接来到关键代码:

    mDecor.startChanging();
    View in = mLayoutInflater.inflate(layoutResource, null);
    decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    mContentRoot = (ViewGroup) in;
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    if (contentParent == null) {  
      throw new RuntimeException("Window couldn't find content container view");
    }
    
    /** * 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;
    

    DecorView通过generateLayout(mDecor)加载到具体的布局文件,具体的布局文件和系统版本以及主题有关,ID_ANDROID_CONTENT对应的即是R.id.content,也是我们通过SetContentView()中设置的布局id,走完installDecor(),我们继续看PhoneWindow中的SetContentView()方法,

    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {  
      final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,getContext());  
      transitionTo(newScene);
     } else {   
     mLayoutInflater.inflate(layoutResID, mContentParent);
     }
    

    接下来就简单了,mLayoutInflater.inflate(layoutResID, mContentParent),因为前面通过installDecor()创建了DecorView,因此这一步就是将Activity的布局(layoutResID)添加到mContentParent中了,到这里为止,Activity的布局文件就已经添加到DecorView里面了,继续看SetcontentView():

    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {  
      cb.onContentChanged();
    }
    

    由于Activity实现了Window的Callback接口,


    U)K7G$(IF36R4BM{TKJ@OM6.png

    并且Activity的布局文件已经添加到DecorView里的mContentParent里了,当执行cb.onContentChanged()后,Activity就会回调onContentChanged()方法,由于Activity的onContentChanged()是个空实现,我们可以在子Activity中处理这个回调处理相应逻辑,到这里为止DecorView就已经被创建并初始化完毕。

    相关文章

      网友评论

        本文标题:Android:从SetContentView()谈起

        本文链接:https://www.haomeiwen.com/subject/saoeettx.html