上一篇博客Android中Handler原理在讲到Handler的时候谈到了android的Activity启动是如何执行到onCreate方法的,这篇主要从onCreate方法里面我们必须要写的方法setContentView开始,研究布局视图是如何加载到手机窗口上的。

当在执行到setContentView时,实际上执行的是

public void setContentView(int layoutResID) {        getWindow().setContentView(layoutResID);        initActionBar();}

可以看到实际上是Window类的setContentView方法

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

Window类是一个抽象类,下面主要是找到他的实现类。mWindow初始化在

final void attach(……) {        ……        mWindow = PolicyManager.makeNewWindow(this);        mWindow.setCallback(this);        mWindow.getLayoutInflater().setPrivateFactory(this);……    }

Attach方法在main方法中。具体查看上一篇博客。调用了PolicyManager类的静态方法makeNewWindow生成Window对象

private static final String POLICY_IMPL_CLASS_NAME =        "com.android.internal.policy.impl.Policy";private static final IPolicy sPolicy;static {        try {            Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);            sPolicy = (IPolicy)policyClass.newInstance();        }……    }public static Window makeNewWindow(Context context) {        return sPolicy.makeNewWindow(context);}

可以看到是通过Policy类的makeNewWindow方法得到的Window对象,这里是通过反射机制获取的Policy的实例。

public Window makeNewWindow(Context context) {        return new PhoneWindow(context);}

可以看到实际上是一个PhoneWindow。那么根据多态,其实在上面调用的就是PhoneWindow#setContentWindow

public void setContentView(int layoutResID) {        if (mContentParent == null) {            installDecor();//1        } else {            mContentParent.removeAllViews();        }        mLayoutInflater.inflate(layoutResID, mContentParent);//2 将layoutResID填充,他的父View是mContentParent是在installDecor方法里面mContentParent = generateLayout(mDecor);得到的        final Callback cb = getCallback();        if (cb != null && !isDestroyed()) {            cb.onContentChanged();        }}

执行到installDecor()

private void installDecor() {    if (mDecor == null) {        mDecor = generateDecor();//生成装饰窗口,装饰窗口继承自FrameLayout        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);        mDecor.setIsRootNamespace(true);        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);        }    }    if (mContentParent == null) {        mContentParent = generateLayout(mDecor);// 产生布局,返回父布局,暂时这样理解,具体进去看代码        mDecor.makeOptionalFitsSystemWindows();        mTitleView = (TextView)findViewById(com.android.internal.R.id.title);        ......        }    }}

generateLayout的代码如下

protected ViewGroup generateLayout(DecorView decor) {        // Apply data from current theme.        TypedArray a = getWindowStyle();// 获取当前设置的主题......        if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) {            requestFeature(FEATURE_NO_TITLE);//可以看到平时在AndroidManifest配置的窗口等各其实在代码里都是在这里修改的        } else if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBar, false)) {            // Don't allow an action bar if there is no title.            requestFeature(FEATURE_ACTION_BAR);        }        if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBarOverlay, false)) {            requestFeature(FEATURE_ACTION_BAR_OVERLAY);        }......//19-63行根据我们指定的有无标题等各种窗口风格得到对应的默认布局,//这些布局在D:\SoftWare\Java\android4.2-source\frameworks\base\core\res\res\layout        int layoutResource;        int features = getLocalFeatures();        if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {            if (mIsFloating) {                TypedValue res = new TypedValue();                getContext().getTheme().resolveAttribute(                        com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true);                layoutResource = res.resourceId;            } else {                layoutResource = com.android.internal.R.layout.screen_title_icons;            }            removeFeature(FEATURE_ACTION_BAR);        } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0                && (features & (1 << FEATURE_ACTION_BAR)) == 0) {            layoutResource = com.android.internal.R.layout.screen_progress;        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {            if (mIsFloating) {                TypedValue res = new TypedValue();                getContext().getTheme().resolveAttribute(                        com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true);                layoutResource = res.resourceId;            } else {                layoutResource = com.android.internal.R.layout.screen_custom_title;            }            removeFeature(FEATURE_ACTION_BAR);        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {            if (mIsFloating) {                TypedValue res = new TypedValue();                getContext().getTheme().resolveAttribute(                        com.android.internal.R.attr.dialogTitleDecorLayout, res, true);                layoutResource = res.resourceId;            } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {                if ((features & (1 << FEATURE_ACTION_BAR_OVERLAY)) != 0) {                    layoutResource = com.android.internal.R.layout.screen_action_bar_overlay;                } else {                    layoutResource = com.android.internal.R.layout.screen_action_bar;                }            } else {                layoutResource = com.android.internal.R.layout.screen_title;            }        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {            layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode;        } else {            layoutResource = com.android.internal.R.layout.screen_simple;        }        mDecor.startChanging();        View in = mLayoutInflater.inflate(layoutResource, null);//根据上面的判断选择的layoutResource填充成View        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));//调用装饰窗口的addView方法将上一步生成的View添加到最外层的装饰窗口        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);//ID_ANDROID_CONTENT对应的都是@android:id/content其实是一个FrameLayout        ......        mDecor.finishChanging();        return contentParent;    }

下面是最常用的layoutResource布局文件他们在D:\SoftWare\Java\android4.2-source\frameworks\base\core\res\res\layout文件夹下

Screen-title.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    android:fitsSystemWindows="true">    <!-- Popout bar for action modes -->    <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" />    <FrameLayout        android:layout_width="match_parent"         android:layout_height="?android:attr/windowTitleSize"        style="?android:attr/windowTitleBackgroundStyle">        <TextView android:id="@android:id/title"             style="?android:attr/windowTitleStyle"            android:background="@null"            android:fadingEdge="horizontal"            android:gravity="center_vertical"            android:layout_width="match_parent"            android:layout_height="match_parent" />    </FrameLayout>    <FrameLayout android:id="@android:id/content"        android:layout_width="match_parent"         android:layout_height="0dip"        android:layout_weight="1"        android:foregroundGravity="fill_horizontal|top"        android:foreground="?android:attr/windowContentOverlay" /></LinearLayout>

最外层是线性布局,包含帧布局(包含一个TextView其实就是标题)和帧布局。其实这个就是最常见的样子。默认生成的程序就是这个样子。

Screen-simple.xml

<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" />    <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>

显而易见这个就是全屏设置时默认加载的布局。

我们可以看到id为content的FrameLayout都存在的,因为他要装载我们填充的xml

依Screen-title.xml为例,大致是这样子的

Android视图加载到窗口的过程分析_第1张图片

通过以上分析明白了以下几点:

1. Activity呈现出来的界面其实是一个PhoneWindow类在管理的,这个类中有一个DecorView成员就是最外层的一个容器。

2. 上面这张图是非常重要的,显示了窗口的结构。

3. Activity到底是个什么东西?还真不好说清楚…^_^总之和刚开始的认识是不同的。

4. 遇到不懂得问题就去查看源码。代码是最好的老师!









更多相关文章

  1. CardView 添加背景图片、改变颜色、水波纹效果的方法
  2. android:layout_alignParent 布局相对于父布局
  3. Android高手进阶教程(十一)--Android 通用获取Ip的方法(判断手机
  4. Android四大布局之表格布局行列位置控制
  5. Android Studio之工程中导入jni库方法
  6. Android取消EditText自动聚焦、自动弹出输入法的方法
  7. 转-Android Studio *.jar 与 *.aar 的生成与*.aar导入项目方法
  8. 从零开始学android开发-布局中 layout_gravity、gravity、orient

随机推荐

  1. 图推荐算法在E&E问题上的应用
  2. 自动驾驶硬件研发的挑战与展望
  3. 携程数据库高可用架构实践
  4. MySQL Load data多种使用方法
  5. flea-frame-db使用之JPA接入
  6. vmware.exe命令行参数详解
  7. 换瓶子算法题
  8. 机器学习,像极了一场足球比赛
  9. I盘格式化了的文件寻回方案.
  10. php+jquery实现无刷新分页和前端端交互操