布局视图是如何加载到手机窗口上的

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

当在执行到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为例,大致是这样子的

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

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

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

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

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

时间: 2024-07-30 13:58:18

布局视图是如何加载到手机窗口上的的相关文章

Android四大组件之Activity(活动)及其布局的创建与加载布局

Android四大组件之Activity(活动)及其布局的创建与加载布局 什么是Activity ? 活动(Activity)是包含用户界面的组件,主要用于和用户进行交互的,一个应用程序中可以包含零个或多个活动. 手动创建Activity的过程详解 到现在为止,你还没有手动创建过活动呢,在第一个安卓工程中,HelloWorldActivity是ADT帮我们创建的,手动创建活动可以加深我们的理解,因此现在是时候应该自己动手了. 首先,你需要再新建一个 Android 项目,项目名可以叫做 Acti

as3.0加载本地或网络上的图片

加载本地或网络上的图片,我们一般只用Loader及URLRequest这两个类就可以完成,URLRequest即可以加载本地的,也可以加载网络的.代码如下 import flash.display.Loader; import flash.net.URLRequest; var loader:Loader = new Loader(); var request:URLRequest = new URLRequest('img/123.png'); loader.y = 200; loader.l

代码中使用bitmap资源并加载到控件上

1.从res/drawable/XX.jpg里引用图片资源: 1. Resources res = getResources(); Bitmap inDrawable= BitmapFactory.decodeResource(res, R.drawable.user_img_grey); userImgButton.setImageBitmap(inDrawable ); 2.从本地sd卡的文件路径中使用图片资源: UserImgPath=Environment.getExternalStor

看到shape文件可以加载到GOOGLE EARTH上的方法,有空可以试试

引用 Shape文件转为KMZ并在Google Earth中显示 (1)在ArcGIS中加载一个Shape文件,笔者加载的是某个地区的道路(双线道路)图层 (2)在ArcToolbox中,依次展开Conversion Tools—>To KML—>Layer to KML, (3)双击Layer to KML,打开Layer to KML对话框,在Layer中设置要进行转换的Shape文件:在Output File中设置输出文件路径及名称:在Layer Output Scale中设置输出比例.

Android时时监测手机的旋转角度 根据旋转角度确定在什么角度加载竖屏布局 在什么时候加载横屏布局

一.场景描述: 近期开发中遇到个问题,就是我们在做横竖屏切换的功能时,横竖屏布局是操作系统去感知的,作为开发员没法确定Activity在什么时候加载横屏布局,在什么时候加载竖屏布局.因此为了找到加载横屏布局与竖屏布局的分界点,我特别监控了屏幕旋转的角度,看在什么样的角度会加载横屏布局,在什么样的角度加载竖屏布局. 二.屏幕旋转度数变化示意图 度数变化,拿着手机顺时针旋转,度数会越变越大. 三.在Activity中监听手机的旋转角度,上代码. /** * 时时监测屏幕方向是否发生改变 * @aut

【Android】纯代码创建页面布局(含异步加载图片)

开发环境:macOS 10.12 + Android Studio 2.2,MinSDK Android 5.1 先看看总体效果 本示例是基于Fragment进行的,直接上代码: [界面结构] 在 Fragment 中,采用 ScrollView + LinearLayout 实现. 1 <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http:/

[Android 性能优化系列]布局篇之动态加载布局

大家如果喜欢我的博客,请关注一下我的微博,请点击这里(http://weibo.com/kifile),谢谢 转载请标明出处(http://blog.csdn.net/kifile),再次感谢 原文地址:http://developer.android.com/training/improving-layouts/loading-ondemand.html 在接下来的一段时间里,我会每天翻译一部分关于性能提升的Android官方文档给大家 性能优化布局篇: [Android 性能优化系列]布局篇

布局文件是怎么加载的

1.布局文件中的控件是怎样被android加载的? 在Activity中的onCreate函数中有一个函数叫做setContentView(R.layout.布局文件); 2.安卓会逐层进行循环,在循环过程中将布局文件中的每一个控件/标签 翻译成的对应的JAVA类,例如将<Button>翻译成Button对象 3.层级越多,布局越复杂,相对应的解析时间就会增加 4.不建议布局超过五层以上 5.大神写代码不屑使用布局文件

布局还能异步加载?AsyncLayoutInflater一点小经验送给你

目录 前言 关于布局加载的两大性能瓶颈,通过IO操作将XML加载到内存中并进行解析和通过反射创建View.当xml文件过大或页面文件过深,布局的加载就会较为耗时.我们知道,当主线程进行一些耗时操作可能就会导致页面卡顿,更严重的可能会产生ANR,所以我们能如何来进行布局加载优化呢?解决这个问题有两种思路,直接解决和侧面缓解.直接解决就是不使用IO和反射等技术(这个我们会在下一节进行介绍).侧面缓解的就是既然耗时操作难以避免,那我们能不能把耗时操作放在子线程中,等到inflate操作完成后再将结果回