前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android View 源码解析(一) - setContentView

Android View 源码解析(一) - setContentView

原创
作者头像
Android架构
修改2019-06-25 18:09:21
7060
修改2019-06-25 18:09:21
举报
文章被收录于专栏:Android进阶之路

本系列主要是探讨View的绘制过程及部分相关的实现机制的源码分析

setContentView分析

相关关系

Activity中有Window成员 实例化为PhoneWindow PhoneWindow是抽象Window类的实现类

Window提供了绘制窗口的通用API PhoneWindow中包含了DecorView对象 是所有窗口(Activity界面)的根View

具体的构如下

具体的可以通过hierarchyviewer工具分析一下

PhoneWindow的setContentView分析

Window类的setContentView方法 而Window的setContentView方法是抽象的 所以查看PhoneWindow的setContentView()

  1. setContentView方法
代码语言:txt
复制
  // 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.
  private ViewGroup mContentParent;

  @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下的所有View
          //又因为这个的存在  我们可以多次使用setContentView()
          mContentParent.removeAllViews();
      }
      //判断是否使用了Activity的过度动画
      if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        //设置动画场景
          final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                  getContext());
          transitionTo(newScene);
      } else {
          //将资源文件通过LayoutInflater对象装换为View树
          //在PhoneWindow的构造函数中 mLayoutInflater = LayoutInflater.from(context);
          mLayoutInflater.inflate(layoutResID, mContentParent);
      }

      //View中
      /**
       * Ask that a new dispatch of {@link #onApplyWindowInsets(WindowInsets)} be performed.
       */
      // public void requestApplyInsets() {
      //     requestFitSystemWindows();
      // }
      mContentParent.requestApplyInsets();
      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 if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
          mContentParent.removeAllViews();
      }

      if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
          view.setLayoutParams(params);
          final Scene newScene = new Scene(mContentParent, view);
          transitionTo(newScene);
      } else {
        //已经为View 直接使用View的addView方法追加到当前mContentParent中
          mContentParent.addView(view, params);
      }
      mContentParent.requestApplyInsets();
      final Callback cb = getCallback();
      //调用CallBack接口的onContentChange来通知Activity组件视图发生了变化
      if (cb != null && !isDestroyed()) {
          cb.onContentChanged();
      }
  }
  1. installDecor方法
代码语言:txt
复制
  //截取部分主要分析代码
  private void installDecor() {
      if (mDecor == null) {
          //如果mDecor为空则创建一个DecorView实例
          // protected DecorView generateDecor() {
          //   return new DecorView(getContext(), -1);
          // }
          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);
          ...
        }
  }
代码语言:txt
复制
  protected ViewGroup generateLayout(DecorView decor) {
       // Apply data from current theme.
       //根据当前style修饰相应样式

       TypedArray a = getWindowStyle();

       ...
       //一堆if判断

       // 增加窗口修饰

       int layoutResource;
       int features = getLocalFeatures();

       ...
       //根据features选择不同的窗帘修饰布局文件得到
       //把选中的窗口修饰布局文件添加到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");
       }

       ...
       return contentParent;
   }

该方法的主要功能为 根据窗口的style为该窗口选择不同的窗口根布局文件 将mDecor作为根视图将窗口布局添加,获取id为content的FrameLayout返回给mContentParent对象 实质为阐释mDecor和mContentParent对象

  1. (扩展)关于设置Activity属性需要在setContentView方法之前调用的问题

在设置Activity属性的时候 比如requestWindowFeature(Window.FEATURE_NO_TITLE) 需要在setContentView方法之前调用

代码语言:txt
复制
  public boolean requestFeature(int featureId) {
      if (mContentParent != null) {
          throw new AndroidRuntimeException("requestFeature() must be called before adding content");
      }
      ...
  }
  1. onContentChanged方法

在PhoneWindow中没有重写getCallback相关方法 而在Window类下

代码语言:txt
复制
  /**
   * Return the current Callback interface for this window.
   */
  public final Callback getCallback() {
      return mCallback;
  }

mCallback相关的赋值方法

代码语言:txt
复制
  /**
   * Set the Callback interface for this window, used to intercept key
   * events and other dynamic operations in the window.
   *
   * @param callback The desired Callback interface.
   */
  public void setCallback(Callback callback) {
      mCallback = callback;
  }

setCallback方法在Activity中被使用

代码语言:txt
复制
  final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
        ...
        mWindow.setCallback(this);
        ...
  }

说明Activity实现了Window的CallBack接口 然后在Activity中找到onContentChanged方法

代码语言:txt
复制
  public void onContentChanged() {
  }

对空方法. 说明在Activity的布局改动时 (setContentView或者addContentView 方法执行完毕后会调用改方法)

所以各种View的findViewById方法什么的可以放在这里

  1. setContentView源码总结
  • 创建一个DecorView的对象mDector 该mDector将作为整个应用窗口的根视图
  • 根据根据Feature等style theme创建不同的窗口修饰布局文件 并且通过findViewById获取Activity布局文件该存放的地方
  • 将Activity的布局文件添加至id为content的FrameLayout内
  • 执行到当前页面还没有显示出来
  1. Activity页面显示

我们都知道Activity的实际开始于ActivityThread的main方法 当该方法调运完之后会调用该类的performLaunchActivity方法来创建要启动的Activity组件 这个过程中还会为该Activity组件创建窗口对象和视图对象 当组件创建完成后用过调用该类的handleResumeActivity方法将其激活

代码语言:txt
复制
  final void handleResumeActivity(IBinder token,
             boolean clearHide, boolean isForward, boolean reallyResume) {
               ...
             if (!r.activity.mFinished && willBeVisible
                     && r.activity.mDecor != null && !r.hideForNow) {
                 ...
                 if (r.activity.mVisibleFromClient) {
                     r.activity.makeVisible();
                     //这里这里 通过调用Activity的makeVisible方法来显示我们通过setContentView创建的mDecor
                 }
                 ...
             }
         } else {
           ...
         }
     }
代码语言:txt
复制
  //Activity的makeVisible方法
  void makeVisible() {
       if (!mWindowAdded) {
           ViewManager wm = getWindowManager();
           wm.addView(mDecor, getWindow().getAttributes());
           mWindowAdded = true;
       }
       mDecor.setVisibility(View.VISIBLE);
   }

至此通过setContentView方法设置的页面才最后显示出来

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • setContentView分析
    • 相关关系
      • PhoneWindow的setContentView分析
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档