Android视图相关之window.getDecorView()源码分析

概述:我们的app中每个页面窗口,都是由Window来表示的;Activity实际上也是一种Window类型(应用Window);每个Activity实例中都有一个Window对象,在attach()方法中被初始化。

public class Activity{

  private Window mWindow;

  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,
            Window window, ActivityConfigCallback activityConfigCallback) {
      ...

      mWindow = new PhoneWindow(this, window, activityConfigCallback);

      ...

}

一、Window类为抽象类,首先查看Window类的类注释
/**
 * Abstract base class for a top-level window look and behavior policy.  An
 * instance of this class should be used as the top-level view added to the
 * window manager. It provides standard UI policies such as a background, title
 * area, default key processing, etc.
 *
 * 

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

Window类是定义窗口外观样式和行为规范的抽象基类,用于作为顶层的View加到WindowManager中;
PhoneWindow是它唯一的实现类。

二、PhoneWindow
  public class PhoneWindow extends Window implements MenuBuilder.Callback{
    private DecorView mDecor;
    ViewGroup mContentParent;

    public PhoneWindow (Context context, Window preservedWindow,
        ActivityConfigCallback activityConfigCallback){
        this(context;)
        mUseDecorContext = true;
        if(preservedWindow != null) {
          mDecor = (DecorView) preservedWindow.getDecorView();
          
          ...

    }

    -->
    @Override
    public final View getDecorView() {
        if (mDecor == null || mForceDecorInstall) {
             installDecor();
        }
         return mDecor;
    }

    private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            mDecor = generateDecor(-1);
            ...
        } else {
              mDecor.setWindow(this);
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
           ...
        }
    }

    -->  
    protected DecorView generateDecor(int featureId) {
         ...
         return new DecorView(context, featureId, this, getAttributes());
    }

    -->  
    protected ViewGroup generateLayout(DecorView decor) {
          // Apply data from current theme.
       
          TypedArray a = getWindowStyle();
          //很长一段都是设置一些Window的属性或者标志;
          ...
      
          // Inflate the window decor.
          //根据设置好的window属性标志来选择layout布局, 赋值到layoutResource 
         int layoutResource;
          int features = getLocalFeatures();
          // System.out.println("Features: 0x" + Integer.toHexString(features));
          if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
              layoutResource = R.layout.screen_swipe_dismiss;
          } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
              if (mIsFloating) {
                  TypedValue res = new TypedValue();
                  getContext().getTheme().resolveAttribute(
                          R.attr.dialogTitleIconsDecorLayout, res, true);
                  layoutResource = res.resourceId;
              } else {
                  layoutResource = R.layout.screen_title_icons;
              }
          } else if ((features & ((1 << FEATURE_PROGRESS) | (1 <<       FEATURE_INDETERMINATE_PROGRESS))) != 0
                  && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
              layoutResource = R.layout.screen_progress;
          } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
              if (mIsFloating) {
                  TypedValue res = new TypedValue();
                  getContext().getTheme().resolveAttribute(
                          R.attr.dialogCustomTitleDecorLayout, res, true);
                  layoutResource = res.resourceId;
              } else {
                  layoutResource = R.layout.screen_custom_title;
              }
          } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
              if (mIsFloating) {
                  TypedValue res = new TypedValue();
                  getContext().getTheme().resolveAttribute(
                          R.attr.dialogTitleDecorLayout, res, true);
                  layoutResource = res.resourceId;
              } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
                   layoutResource = a.getResourceId(
                          R.styleable.Window_windowActionBarFullscreenDecorLayout,
                          R.layout.screen_action_bar);
              } else {
                  layoutResource = R.layout.screen_title;
              }
              // System.out.println("Title!");
           } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
              layoutResource = R.layout.screen_simple_overlay_action_mode;
          } else {
              layoutResource = R.layout.screen_simple;
          }
       
          mDecor.startChanging();//mChanging = true;修改标志
          mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);//重点代码,请看下一段

          ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
          if (contentParent == null) {
              throw new RuntimeException("Window couldn't find content container view");
          }
        
          ...
          mDecor.finishChanging();
  
          return contentParent;
      }
  }


  //DecorView是个FrameLayout
  public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
  }
(1)getDecorView()分析:
  • getDecorView()调用了installDecor(),
  • installDecor()方法中首先调用了generateDecor(), 创建一个DecorView赋值给mDecor,DecorView继承自FrameLayout;
  • 然后调用generateLayout()设置window的一些属性,根据属性给layoutResource赋值对应的layoutId,然后执行mDecor.onResourcesLoaded() , 把layoutResource布局绘制成view添加到mDecor中;
  • 从layoutResource布局中找到id为android:id/content的ViewGroup,赋值给wmContentParent;
(2)onResourcesLoaded(LayoutInflater inflater, int layoutResource)
  //onResourcesLoaded主要是把layoutResource inflate出来,添加到DecorView中
   void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
        mStackId = getStackId();
        //mBackdropFrameRenderer 用于绘制背景的线程,不为空时先绘制DecorView的背景
        if (mBackdropFrameRenderer != null) {
            loadBackgroundDrawablesIfNeeded();
            mBackdropFrameRenderer.onResourcesLoaded(
                    this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
                    mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
                    getCurrentColor(mNavigationColorViewState));
        }

        //window的标题View,显示与否取决于window的类型和作用场景
        mDecorCaptionView = createDecorCaptionView(inflater);
        final View root = inflater.inflate(layoutResource, null);//创建这个layoutResource 的 View 实例对象 root
        if (mDecorCaptionView != null) {
          // 先添加 mDecorCaptionView(如果不为null),  再向 mDecorCaptionView 中添加 root
            if (mDecorCaptionView.getParent() == null) {
                addView(mDecorCaptionView,
                        new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
            }
            mDecorCaptionView.addView(root,
                    new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
        } else {

            // Put it below the color views.
            addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
        mContentRoot = (ViewGroup) root;
        initializeElevation();
    }

分析:
这里的重点是把layoutResource的布局绘制成View对象root,然后把root添加为mDecor的子view。

(3)查看一下其中的一些布局(寻找android:id/content)

R.layout.screen_swipe_dismiss


R.layout.screen_custom_title


    
    

    
    
    

R.layout.screen_simple


    
    

上面三个布局都有id为android:id/content的控件

总结 PhoneWindow.getDecorView()
一、调用mWindow.getDecorView()方法时,如果mDecor 为空,执行installDecor(),步骤如下:
1.执行generateDecor()方法,创建一个DecorView的实例,赋值到变量mDecor ;
2.执行generateLayout(DecorView decor)方法,把mDecor 传递进去;
(2.1)获取主题设置,配置一些Window的属性标志;
(2.2)根据Window的属性标志,给layoutResource选择对应的layout赋值,这些layout的共同点是都包含一个id为@android:id/content的layout;
(2.3)把2.2步骤的layoutResource绘制出来变成view,添加为DecorView的子view(mContentParent和DecorView中间很可能还有一层LinearLayout,要看布局的选择)
(2.4)从layoutResource中找到id为android:id/content的ViewGroup对象,赋值给mContentParent;
二、如果mDecor 不为空(这时Activity已经完成setContentView的方法),返回以DecorView为根布局的整个视图layout(包括我们setContentView中设置的layout)。

Android视图相关之window.getDecorView()源码分析_第1张图片
Window视图层级.png

你可能感兴趣的:(Android视图相关之window.getDecorView()源码分析)