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

Android setContentView与LayoutInflater加载解析机制源码分析
  • Activity 和 Window之间的关系

    • Activity 内有三个 setContentView重载的方法

        public void setContentView(int layoutResID) {
            getWindow().setContentView(layoutResID);
            initWindowDecorActionBar();
        }
      
        public void setContentView(View view) {
            getWindow().setContentView(view);
            initWindowDecorActionBar();
        }
      
        public void setContentView(View view, ViewGroup.LayoutParams params) {
            getWindow().setContentView(view, params);
            initWindowDecorActionBar();
        } 
      
    • Activity 内维护了一个Window --

      Window 是一个抽象类 有个实现类 PhoneWindow PhoneWindow 内部维护了一个DecorWindow

        Window是一个抽象类,提供了绘制窗口的一组通用API。
      
        PhoneWindow是Window的具体继承实现类。而且该类内部包含了一个DecorView对象,该DectorView对象是所有应用窗口(Activity界面)的根View。
      
        DecorView是PhoneWindow的内部类,是FrameLayout的子类,是对FrameLayout进行功能的修饰(所以叫DecorXXX),是所有应用窗口的根View 
      
  • 窗口PhoneWindow类的setContentView方法

      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)) {
      		//应用程序里可以多次调用setContentView()来显示界面,因为会removeAllViews。
              mContentParent.removeAllViews();
          }
    
          if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
              final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                      getContext());
              transitionTo(newScene);
          } else {
              mLayoutInflater.inflate(layoutResID, mContentParent);
          }
          final Callback cb = getCallback();
          if (cb != null && !isDestroyed()) {
              cb.onContentChanged();
          }
      }
    
      -->
      private void installDecor() {
          if (mDecor == null) {
      		//首先判断mDecor对象是否为空,如果为空则调用generateDecor()
              mDecor = generateDecor();
              mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
              mDecor.setIsRootNamespace(true);
              if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                  mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
              }
          }
          if (mContentParent == null) {
              //根据窗口的风格修饰,选择对应的修饰布局文件,并且将id为content的FrameLayout赋值给mContentParent
              mContentParent = generateLayout(mDecor);
              //......
              //初始化一堆属性值
          }
       }
    
      -->
      protected DecorView generateDecor() {
     		 return new DecorView(getContext(), -1);
      }
      generateDecor()创建一个DecorView(该类是 FrameLayout子类,即一个ViewGroup视图);
      -->
      //generateDecor执行完之后会执行generateLayout 
      protected ViewGroup generateLayout(DecorView decor) {
          // Apply data from current theme.
    
          TypedArray a = getWindowStyle();
    
          //......
          //依据主题style设置一堆值进行设置
    
          // Inflate the window decor.
    
          int layoutResource;
          int features = getLocalFeatures();
          //......
          //根据设定好的features值选择不同的窗口修饰布局文件,得到layoutResource值
    
          //把选中的窗口修饰布局文件添加到DecorView对象里,并且指定contentParent值
          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");
          }
    
          //......
          //继续一堆属性设置,完事返回contentParent
          return contentParent;
       }
    
      根据窗口的风格修饰类型为该窗口选择不同的窗口根布局文件。mDecor做为根视图将该窗口根布局添加进去,然后获取id为content的FrameLayout返回给mContentParent对象。所以installDecor方法实质就是产生mDecor和mContentParent对象。
      我们平时requestWindowFeature()设置的值就是在这里通过getLocalFeature()获取的;而android:theme属性也是通过这里的getWindowStyle()获取的。
    
  • 总结过程

    • 创建一个DecorView的对象mDecor,该mDecor对象将作为整个应用窗口的根视图。

    *依据Feature等style theme创建不同的窗口修饰布局文件,并且通过findViewById获取Activity布局文件该存放的地方(窗口修饰布局文件中id为content的FrameLayout)。

    • 将Activity的布局文件添加至id为content的FrameLayout内。

    • 至此整个setContentView的主要流程就分析完毕。

View二三
  • LayoutInflator

    可以将一个布局达成一个View对象,该类会调用一系列方法,底层使用Pull解析 解析出每个View的TAg 调用createViewFromTag(),该方法又会调用createView 方法 通过反射的方式 创建出View的对象并返回。

  • LayoutInflator 打出来的布局 为什么宽高属性会失效

    首先View必须存在于一个布局中,之后如果将layout_width 设置成 match_parent表示让View的宽度填充满布局,如果 设置成 wrap_content表示让View的宽度刚好可以包含其内容,如果设置成具体的数值则View的宽度会变成相应的数值。这也是为什么这两个属性叫作layout_width和layout_height,而不是width和height。

    这主要是因为,在setContentView()方法中,Android会自动在布局文件的最外层再嵌套一个FrameLayout,所以layout_width和layout_height属性才会有效果

转载于:https://my.oschina.net/caipeng/blog/841476

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值