Android 源代码解析 之 setContentView

大家在平时的开发中。对于setContentView肯定不陌生,那么对其内部的实现会不会比較好奇呢~~~有幸最终能看到一些PhoneWindow神马的源代码,今天就带大家来跑一回源代码~~

1、Activity  setContentView

首先不用说,进入Activity的setContentView

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

能够看到里面获取了Window。然后调用了Window的setContentView

2、PhoneWindow  setContentView

这里的Window的实现类是PhoneWindow(package com.android.internal.policy.impl;)。我们直接看它的实现:

 @Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mLayoutInflater.inflate(layoutResID, mContentParent);
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }

能够看到,首先推断mContentParent是否为null,是则调用installDecor(),否则移除其内部全部的子Views,然后通过LayoutInflater.inflate将我们传入的layout放置到mContentParent中。

从这里就能看出来mContentParent是个ViewGroup且包裹我们整个布局文件;而installDecor()预计就是去初始化我们这个mContentParent。一会我们会去验证。

接下来,通过getCallBack拿到了一个CallBack对象。事实上这个获取到的这个CallBack就是我们Activity自己,你能够去看我们的Activity是实现了CallBack接口的。

这个Callback明显就是一个回调,当PhoneWindow接收到系统分发给它的触摸、IO、菜单等相关的事件时,能够回调对应的Activity进行处理。至于Callback能够回调哪些方法,自己看下这个接口的声明方法就可以。

当然了这里不是我们的关键,由于我们的setContentView里面仅仅是回调了onContentChanged,而onContentChanged在Activity中是空实现。

好了,接下来去看我们的installDecor()

3、PhoneWindow  installDecor

private void installDecor() {
	        if (mDecor == null) {
	            mDecor = generateDecor();
	            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
	            mDecor.setIsRootNamespace(true);
	            //...
	            }
	        }
	        if (mContentParent == null) {
	            mContentParent = generateLayout(mDecor);
	            mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
	            if (mTitleView != null) {
	               //依据FEATURE_NO_TITLE隐藏,或者设置mTitleView的值
	            	//...
	            } else {
	                mActionBar = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
	                if (mActionBar != null) {
	                	//设置ActionBar标题、图标神马的;依据FEATURE初始化Actionbar的一些显示
	                	//...
	                }
	            }
	        }
	}

这里代码比較长,删除了一些初始化Actionbar样式神马的代码。

能够看到这里不仅初始化mContentParent,并且在之前先调用generateDecor();初始化了一个mDecor,mDecor是DecorView对象。为FrameLayout的子类。

在得到mDecor以后设置其焦点的获取方式为,当其子孙都不须要时,自己才获取。

然后通过 generateLayout(mDecor);把mDecor做为參数传入。然后获取到了我们的mContentParent;

接下里就開始通过findViewById进行获取控件了,而这里的findViewById的代码是这种:

 public View findViewById(int id) {
        return getDecorView().findViewById(id);
    }

getDecorView返回的就是我们的mDecor。

这里我们推測下,首先去初始化mDecor,然后通过mDecor初始化了mContentParent。接下来mDecor就能够使用findViewById方法了。那么我认为,在初始化mDecor的方法

generateDecor()中。一定为我们的mDecor放入了布局或者控件(最简单的就是使用inflate压入了布局文件)。而mContentParent可能就是mDecor中的某个子View。

是不是这样呢?

我们一起来先看看generateDecor()方法的实现:

4、PhoneWindow  generateDecor

protected DecorView generateDecor() {
        return new DecorView(getContext(), -1);
    }
  public DecorView(Context context, int featureId) {
            super(context);
            mFeatureId = featureId;
        }

非常遗憾,我们的generateDecor()仅仅是初始化了一个FrameLayout对象,并没有在其内部压入布局文件,看来我们的推測有些问题;只是没事,既然此方法没有,那么generateLayout(mDecor);中一定设置了layout文件。并且这名字也非常像这么回事。

5、PhoneWindow  generateLayout

protected ViewGroup generateLayout(DecorView decor) {
			        // Apply data from current theme.
			        TypedArray a = getWindowStyle();
			        //...Window_windowIsFloating,Window_windowNoTitle。Window_windowActionBar...
			        //首先通过WindowStyle中设置的各种属性。对Window进行requestFeature或者setFlags

			        if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) {
			            requestFeature(FEATURE_NO_TITLE);
			        }
			        //...
			        if (a.getBoolean(com.android.internal.R.styleable.Window_windowFullscreen, false)) {
			            setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
			        }
			        //...依据当前sdk的版本号确定是否须要menukey
			        WindowManager.LayoutParams params = getAttributes();
			        //通过a中设置的属性,设置  params.softInputMode 软键盘的模式;
			        //假设当前是浮动Activity。在params中设置FLAG_DIM_BEHIND并记录dimAmount的值。
			        //以及在params.windowAnimations记录WindowAnimationStyle

			        // Inflate the window decor.
			        int layoutResource;
			        int features = getLocalFeatures();
			        // System.out.println("Features: 0x" + Integer.toHexString(features));
			        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;
			            }
			            // 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 = com.android.internal.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(
			                        com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true);
			                layoutResource = res.resourceId;
			            } else {
			                layoutResource = com.android.internal.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(
			                        com.android.internal.R.attr.dialogTitleDecorLayout, res, true);
			                layoutResource = res.resourceId;
			            } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
			                layoutResource = com.android.internal.R.layout.screen_action_bar;
			            } else {
			                layoutResource = com.android.internal.R.layout.screen_title;
			            }
			            // System.out.println("Title!");
			        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
			            layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode;
			        } else {
			            // Embedded, so no decoration is needed.
			            layoutResource = com.android.internal.R.layout.screen_simple;
			            // System.out.println("Simple!");
			        }

			        View in = mLayoutInflater.inflate(layoutResource, null);
			        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));

			        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
			        //...

			        return contentParent;
			    }
	     }

代码也比較长。首先getWindowStyle在当前的Window的theme中获取我们的Window中定义的属性。详细參考:\frameworks\base\core\res\res\values\attrs.xml

 <!-- The set of attributes that describe a Windows‘s theme. -->
    <declare-styleable name="Window">
        <attr name="windowBackground" />
        <attr name="windowContentOverlay" />
        <attr name="windowFrame" />
        <attr name="windowNoTitle" />
        <attr name="windowFullscreen" />
        <attr name="windowOverscan" />
        <attr name="windowIsFloating" />
        <attr name="windowIsTranslucent" />
        <attr name="windowShowWallpaper" />
        <attr name="windowAnimationStyle" />
        <attr name="windowSoftInputMode" />
        <attr name="windowDisablePreview" />
        <attr name="windowNoDisplay" />
        <attr name="textColor" />
        <attr name="backgroundDimEnabled" />
        <attr name="backgroundDimAmount" />

然后就依据这些属性的值。对我们的Window各种requestFeature,setFlags等等。

所以这里就是解析我们为Activity设置theme的地方,至于theme一般能够在AndroidManifest里面进行设置。

接下来就到关键的部分了。21-75行:通过对features和mIsFloating的推断,为layoutResource进行赋值,至于值能够为R.layout.screen_custom_title;R.layout.screen_action_bar;等等。

至于features。除了theme中设置的,我们也能够在Activity的onCreate的setContentView之前进行requestFeature,也解释了,为什么须要在setContentView前调用requestFeature设置全屏什么的。

得到了layoutResource以后,78行,通过LayoutInflater把布局转化成view,增加到我们的decor,即传入的mDecor中。

接下来81行:通过mDecor.findViewById传入R.id.content(相信这个id大家或多或少都听说过),返回mDecor(布局)中的id为content的View,一般为FrameLayout。

好了。能够看到我们的mDecor是一个FrameLayout,然后会依据theme去选择系统中的布局文件。将布局文件通过inflate转化为view。增加到mDecor中。这些布局文件里都包括一个id为content的FrameLayout。将其引用返回给mContentParent。

等我们的mContentParent有值了以后。还记得干嘛了么?再贴一次PhoneWindow的setContentView

  @Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mLayoutInflater.inflate(layoutResID, mContentParent);
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }

有了mContentParent,然后把我们写的布局文件通过inflater增加到mContentParent中。

关于R.layout.xxx能够在frameworks\base\core\res\res\layout里面进行查看。

比如:R.layout.screen_custom_title.xml

<?

xml version="1.0" encoding="utf-8"?>

<!--
This is a custom layout for a screen.
-->

<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:id="@android:id/title_container"
        android:layout_width="match_parent"
        android:layout_height="?

android:attr/windowTitleSize"
        style="?android:attr/windowTitleBackgroundStyle">
    </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>

上面的title_container是用来放自己定义Title的容器,而以下的content就是放置我们设置的布局的容器。关于自己定义Title样例,大家能够百度下。

到此,我们的setContentView就分析完毕了。我们能够回想一下:

首先初始化mDecor,即DecorView为FrameLayout的子类。就是我们整个窗体的根视图了。

然后。依据theme中的属性值,选择合适的布局。通过infalter.inflater放入到我们的mDecor中。

在这些布局中,通常会包括ActionBar。Title,和一个id为content的FrameLayout。

最后,我们在Activity中设置的布局,会通过infalter.inflater压入到我们的id为content的FrameLayout中去。

----------------------------------------------------------------------------------------------------------

博主部分视频已经上线。假设你不喜欢枯燥的文本。请猛戳(初录,期待您的支持):

1、Android 自己定义控件实战 电商活动中的刮刮卡

2、Android自己定义控件实战  打造Android流式布局和热门标签

3、Android智能机器人“小慕”的实现

4、高仿QQ5.0側滑

5、高仿微信5.2.1主界面及消息提醒

时间: 2024-08-01 21:15:28

Android 源代码解析 之 setContentView的相关文章

Android源代码解析之(十三)--&amp;gt;apk安装流程

转载请标明出处:一片枫叶的专栏 上一篇文章中给大家分析了一下android系统启动之后调用PackageManagerService服务并解析系统特定文件夹.解析apk文件并安装的过程,这个安装过程实际上是没有图形界面的,底层调用的是我们平时比較熟悉的adb命令,那么我们平时安装apk文件的时候大部分是都过图形界面安装的,那么这样的方式安装apk详细的流程是如何的呢? 本文我们就来详细看一下apk的详细安装过程,通过本文的学习希望帮助大家大概的了解到Android系统安装Apk文件的基本流程.好

Android源代码解析之(三)--&amp;gt;异步任务AsyncTask

转载请标明出处:一片枫叶的专栏 上一篇文章中我们解说了android中的异步消息机制. 主要解说了Handler对象的使用方式.消息的发送流程等.android的异步消息机制是android中多任务处理的基础,Handler是整个android应用层体系异步消息传递的基础组件,通过对Handler源代码的解析的解析相信大家对android中的异步消息机制有了一个大概的了解.很多其它关于android中的异步消息机制的知识可參考我的:android源代码解析之(二)–>异步消息机制 android

Android源代码解析之(四)--&amp;gt;HandlerThread

转载请标明出处:一片枫叶的专栏 上一篇文章中我们解说了AsyncTast的基本使用以及实现原理,我们知道AsyncTask内部是通过线程池和Handler实现的.通过对线程池和handler的封装实现了对异步任务操作.很多其它关于AsyncTask相关的内容,可參考我的android源代码解析之(三)–>异步任务AsyncTask 本文我们将解说HandlerThread相关的概念. HandlerThread是什么东西呢?了解一个类最好的方法就是查看类的定义,所以我们就看一下HandlerTh

Android源代码解析之(六)--&amp;gt;Log日志

转载请标明出处:一片枫叶的专栏 首先说点题外话,对于想学android framework源代码的同学,事实上能够在github中fork一份,详细地址:platform_frameworks_base 这里面基本都是android framework层的源代码了.并且近期发现了一个比較不错的github插件:OctoTree,它 是一个浏览器插件,它能够让你在Github 看代码时,左边栏会出现一个树状结构.就像我们在IDE 一样.当我们看一个项目的结构,或者想看详细的某个文件,这样就会非常方

Android源代码解析之(七)--&amp;gt;LruCache缓存类

转载请标明出处:一片枫叶的专栏 android开发过程中常常会用到缓存.如今主流的app中图片等资源的缓存策略通常是分两级.一个是内存级别的缓存,一个是磁盘级别的缓存. 作为android系统的维护者google也开源了其缓存方案,LruCache和DiskLruCache.从android3.1開始LruCache已经作为android源代码的一部分维护在android系统中.为了兼容曾经的版本号android的support-v4包也提供了LruCache的维护,假设App须要兼容到andr

Android View体系(八)从源代码解析View的layout和draw流程

相关文章 Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源代码解析Scroller Android View体系(五)从源代码解析View的事件分发机制 Android View体系(六)从源代码解析Activity的构成 Android View体系(七)从源代码解析View的measure流程 前言 上一篇文章我们讲了View的measure的流程.接

《Android源代码设计模式解析与实战》读书笔记(十)

第十章.解释器模式 解释器模式是一种用的比較少的行为型模式.其提供了一种解释语言的语法或表达式的方式. 可是它的使用场景确实非常广泛,仅仅是由于我们自己非常少回去构造一个语言的文法,所以使用较少. 1.定义 给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子. (当中语言就是我们须要解释的对象,文法就是这个语言的规律,解释器就是翻译机.通过文法来翻译语言.) 2.使用场景 1.假设某个简单的语言须要解释运行并且能够将该语言中的语句表示为一个抽象的语法树时

Android SVG动画PathView源代码解析与使用教程(API 14)

使用的是一个第三方库android-pathview主要是一个自己定义View--PathView.跟全部自己定义View一样,重写了三个构造方法. 而且终于调用三个參数的构造方法,在里面获取自己定义属性. /** * Default constructor. * * @param context The Context of the application. */ public PathView(Context context) { this(context, null); } /** * D

《Android源代码设计模式解析与实战》读书笔记

1.定义 将对象组合成树形结构以表示"部分-总体"的层次结构,使得用户对单个对象和组合对象的使用具有一致性. 2.使用场景 (1)表示对象的部分-总体层次结构时. (2)从一个总体中可以独立出部分模块或功能的场景. 3.UML类图 (1)Component:抽象根节点,为组合中的对象声明接口.在适当的情况下.实现全部类共同拥有接口的缺省行为. 声明一个接口用于訪问和管理Component的子节点.可在递归结构中定义一个接口,用于訪问一个父节点,并在合适的情况下实现它. (2)Compo