Android视图载入到窗体的过程分析

上一篇博客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为例。大致是这样子的

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2F1Y2h5d2VpZXJzdHJhc3M=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" >

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

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

2. 上面这张图是很重要的,显示了窗体的结构。

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

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

时间: 2024-12-31 08:31:17

Android视图载入到窗体的过程分析的相关文章

Android视图框架

Android视图框架 Android的UI系统是android应用系统框架最核心,最基础的内容! 1. Android视图系统.层次关系 Android应用设计和Web应用设计类似,也分前端和后端设计.Android的核心要素和四大组件属于后端设计部分,UI设计属于前端设计.前端设计决定了用户体验的好坏,后端设计则决定了功能的完备和应用的安全.稳定. 对Android的UI设计来说,用到的最重要的两个类是:View和ViewGroup.它们决定着展示给用户的外观界面的形状.下面介绍下Andro

Android布局文件的加载过程分析:Activity.setContentView()源码分析

大家都知道在Activity的onCreate()中调用Activity.setContent()方法可以加载布局文件以设置该Activity的显示界面.本文将从setContentView()的源码谈起,分析布局文件加载所涉及到的调用链.本文所用的源码为android-19. Step 1  .Activity.setContentView(intresId) public void setContentView(int layoutResID) { getWindow().setConten

Android异步载入全解析之大图处理

Android异步载入全解析之大图处理 异步载入中很重要的一部分就是对图像的处理,这也是我们前面用异步载入图像做示例的原因. 一方面是由于图像处理不好的话会很占内存,并且easyOOM,还有一方面,图像也比文字要大,载入比較慢.所以,在解说了怎样进行多线程.AsyncTask进行多线程载入后,先暂停下后面的学习.来对图像的异步处理进行一些优化工作. 为什么要对图像处理 为什么要对图像进行处理,这是一个很直接的问题.一张图像.无论你拿手机.相机.单反还是什么玩意拍出来,它就有一定的大小,可是在不同

Android异步载入全解析之使用多线程

异步载入之使用多线程 初次尝试 异步.异步,事实上说白了就是多任务处理.也就是多线程执行.多线程那就会有各种问题,我们一步步来看.首先.我们创建一个class--ImageLoaderWithoutCaches,从命名上.大家也看出来,这个类,我们实现的是不带缓存的图像载入,不多说,我们再创建一个方法--showImageByThread,通过多线程来载入图像: /** * Using Thread * @param imageView * @param url */ public void s

android编程之悬浮窗体

用过手机360和QQ手机管家等一些软件的朋友,会发现,在这些应用中,会出现一个悬浮窗体,例如QQ手机管家中打电话的场景: 这种窗体除了会显示外,还可以移动它的位置,并且一直显示.除了关闭当前程序外,窗口不会主动消失.其实,它的使用原理也很简单,就是借用了WindowManager这个管理类来实现的.注意:要在AndroidManifest.xml中添加使用权限: [html] view plaincopy <uses-permission android:name="android.per

android视图概述

android视图概述 一.简介 数据和控件分开的作用: 便于引用 便于修改:修改的时候直接改一次数据就可以了

Android异步载入全解析之开篇瞎扯淡

Android异步载入 概述 Android异步载入在Android中使用的很广泛,除了是由于避免在主线程中做网络操作.更是为了避免在显示时由于时间太长而造成ANR,添加显示的流畅性,特别是像ListView.GridView这种控件.假设getView的时间太长,就会造成很严重的卡顿,很影响性能. 本系列将展示在Android中怎样进行异步载入操作,并使用ListView来作为演示的对象. 怎样下载图像 下载自然是须要使用网络,使用网络就不能在主线程.在主线程就会爆炸.所以我们必须要在非主线程

Android异步载入全解析之IntentService

Android异步载入全解析之IntentService 搞什么IntentService 前面我们说了那么多,异步处理都使用钦定的AsyncTask.再不济也使用的Thread,那么这个IntentService是个什么鬼. 相对与前面我们提到的这两种异步载入的方式来说.IntentService有一个最大的特点.就是--IntentService不受大部分UI生命周期的影响.它为后台线程提供了一个更直接的操作方式.只是,IntentService的不足主要体如今下面几点: 不能够直接和UI做

1.Android 视图及View绘制分析笔记之setContentView

自从1983年第一台图形用户界面的个人电脑问世以来,几乎所有的PC操作系统都支持可视化操作,Android也不例外.对于所有Android Developer来说,我们接触最多的控件就是View.通常,我们使用自定义View,需要了解最多的除了事件分发,就是View的绘制过程.然而关于View的绘制,涉及到的知识点纷繁复杂,这么多的代码知识,要梳理起来,肯定是先要找个头.那么平常我们用的最多的方法是哪个方法呢?当然是setContentView()! setContentView 首先我们直接在