美文网首页
从Activity的setContent()方法分析 Windo

从Activity的setContent()方法分析 Windo

作者: Perk | 来源:发表于2018-01-28 22:14 被阅读0次

Activity的setContentView()

public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

setContentView很简单,调用了两个方法。

分析第一个方法:

    getWindow().setContentView(layoutResID);

这个方法返回一个Window对象mWindow,Window是一个抽象类,它的setContentView()方法是一个抽象方法。

private Window mWindow;
public Window getWindow() {
    return mWindow;
}

既然Activity用到了这个抽象的Window,在Activity里面一定有它的实例化过程。

final void attach( 
  //有很多参数,省略了
  ) {
 //有很多代码,省略了    
    mWindow = new PhoneWindow(this, window);
}

在Activity的attach()方法里面new了一个PhoneWindow(),那么这个PhoneWindow就是Window的实现类了。

注:在不同sdk版本中Window类的实例化过程不一样,本人用的SDK版本是SDK25。从SDK23开始,Window示例是通过new PhoneWindow()方式获取的。在SDK22及之前版本,是通过PolicyManager.makeNewWindow()获取的,具体如下。

final void attach( 
  //有很多参数,省略了
  ) {
 //有很多代码,省略了    
 mWindow = PolicyManager.makeNewWindow(this);
}

不管怎么样,最终都得到了这个PhoneWindow的实例。在PhoneWindow里面实现了setContentView()方法。

@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
    // 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();//注释一:创建DecorView
    } 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 {
        mContentParent.addView(view, params);
    }
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
}

在上面代码的注释一中,installDecor()方法最终会创建DecorView实例。DecorView是ViewGroup里面的一个内部类,是顶层View容器,可以查看文章最后的关系图了解。

private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor();//注释1:创建DecorView.
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);//注释2:mContentParent是一个ViewGroup

            // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
            mDecor.makeOptionalFitsSystemWindows();

            final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
                    R.id.decor_content_parent);

            if (decorContentParent != null) {
                mDecorContentParent = decorContentParent;
                mDecorContentParent.setWindowCallback(getCallback());
                if (mDecorContentParent.getTitle() == null) {
                    mDecorContentParent.setWindowTitle(mTitle);
                }

                final int localFeatures = getLocalFeatures();
                for (int i = 0; i < FEATURE_MAX; i++) {
                    if ((localFeatures & (1 << i)) != 0) {
                        mDecorContentParent.initFeature(i);
                    }
                }
//省略很多代码
         }
            }
        }
    }

我们来看installDecor()的注释1代码:installDecor()方法调用generateDecor();创建DecorView实例。

protected DecorView generateDecor() {
    return new DecorView(getContext(), -1);
}

这里new 了一个DecorView();DecorView是PhoneWindow类的内部类,继承自FrameLayout。

我们再来看看installDevor()方法的注释2代码:installDecor()方法调用generateLayout()获取mContentParent对象,它是一个ViewGroup。

protected ViewGroup generateLayout(DecorView decor) {
    // Apply data from current theme.

    TypedArray a = getWindowStyle();

 
 //省略200多行代码


//填充window decor
    // Inflate the window decor.

    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;
        }
        // XXX Remove this once action bar supports these features.
        removeFeature(FEATURE_ACTION_BAR);
        // System.out.println("Title Icons!");
    } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
            && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
        // Special case for a window with only a progress bar (and title).
        // XXX Need to have a no-title version of embedded windows.
        layoutResource = R.layout.screen_progress;
        // System.out.println("Progress!");
    } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
        // Special case for a window with a custom title.
        // If the window is floating, we need a dialog layout
        if (mIsFloating) {
            TypedValue res = new TypedValue();
            getContext().getTheme().resolveAttribute(
                    R.attr.dialogCustomTitleDecorLayout, res, true);
            layoutResource = res.resourceId;
        } else {
            layoutResource = R.layout.screen_custom_title;
        }
        // XXX Remove this once action bar supports these features.
        removeFeature(FEATURE_ACTION_BAR);
    } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
        // If no other features and not embedded, only need a title.
        // If the window is floating, we need a dialog layout
        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 {
        // Embedded, so no decoration is needed.
        layoutResource = R.layout.screen_simple;//资源名称
        // System.out.println("Simple!");
    }

    mDecor.startChanging();
    //将xml资源加载到LayoutInflater里面。
    View in = mLayoutInflater.inflate(layoutResource, null);
   //将加载了资源的View添加到DecorView中
    decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
   //将这个视图设为根View。
   mContentRoot = (ViewGroup) in;
  //获取一个Id为ID_ANDROID_CONTENT的ViewGroup,通过查看上面给出的Android系统的R.layout.screen_simple
  //这个资源文件可以发现,它是一个FrameLayout。
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    if (contentParent == null) {
        throw new RuntimeException("Window couldn't find content container view");
    }

 

 

    // Remaining setup -- of background and title -- that only applies
    // to top-level windows.
    if (getContainer() == null) {
        final Drawable background;
        if (mBackgroundResource != 0) {
            background = getContext().getDrawable(mBackgroundResource);
        } else {
            background = mBackgroundDrawable;
        }
        mDecor.setWindowBackground(background);

        final Drawable frame;
        if (mFrameResource != 0) {
            frame = getContext().getDrawable(mFrameResource);
        } else {
            frame = null;
        }
        mDecor.setWindowFrame(frame);

        mDecor.setElevation(mElevation);
        mDecor.setClipToOutline(mClipToOutline);

        if (mTitle != null) {
            setTitle(mTitle);
        }

        if (mTitleColor == 0) {
            mTitleColor = mTextColor;
        }
        setTitleColor(mTitleColor);
    }

    mDecor.finishChanging();

    return contentParent;
}

我们查看Sdk\platforms\android-版本\data\res\layout目录下的screen_simple.xml文件。发现这个ID为content的ViewGroup是一个FrameLayout。


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />
    <FrameLayout
         android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foregroundInsidePadding="false"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

我们再来看下screen_simple_overlay_action_mode.xml这个文件


<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">
    <FrameLayout
         android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foregroundInsidePadding="false"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />
</FrameLayout>

发现父布局不同,一个是LinearLayout,一个是FrameLayout。

下面是Activity PhoneWindow DecorView 之间的关系图。

image.png

主要流程图:

image.png

相关文章

网友评论

      本文标题:从Activity的setContent()方法分析 Windo

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