分析View

setContentView

首先我们直接在Android Studio中找到一个Activity(请注意,本文分析的是Activity,如果你看的是AppCompatActivity,实际代码会有出入),然后找到setContent方法然后点进去,我们可以看到

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

然后查找getWindow()方法

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

得知调用的是Window类的setContent()方法。然后再全类搜索mWindow,在attach方法中找到了赋值语句。

final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window) {
···
mWindow = new PhoneWindow(this, window);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
···
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();
···
}

然后就引出了我们今天主要分析的对象PhoneWindow。

PhoneWindow

查找PhoneWindow的setContentView方法,可以看到有三个重载方法。

@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
} if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
} @Override
public void setContentView(View view) {
setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
} @Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
if (mContentParent == null) {
installDecor();
} 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);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}

三个方法大体的处理流程是:

  1. 初始化DecorView
  2. 检查并处理转场动画
  3. 将实际要显示的Layout或者View添加到mContentParent中
  4. 通知Callback(即Activity)调用onContentChanged方法

DecorView

在上面的流程里面,相对比较重要的就是第一步的初始化DecorView,也就是installDecor()方法,下面我们继续分析这个方法。

private void installDecor() {
···
if (mDecor == null) {
mDecor = generateDecor(-1);
···
}
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
···
}
···
}

installDecor()方法里,主要做的就是两件事,一个是generateDecor,生成mDecor,还有一个是generateLayout,生成mContentParent。generateDecor中,没有太多复杂逻辑,就是做一些判断,然后实例化出来一个DecorView,这里需要说一点的就是在Android7.0以前,DecorView是PhoneWindow的内部类,7.0以后,DecorView单独提出来变成了一个类,所以如果有用到反射的话,这里可能会出现问题,需要做好版本判断。然后我们看下generateLayout的逻辑:

protected ViewGroup generateLayout(DecorView decor) {
//通过系统获取样式
TypedArray a = getWindowStyle();
//然后一系列的判断,获取样式里的属性,然后设置features
//例如是否悬浮,是否有Title等等
···
int layoutResource;
int features = getLocalFeatures();
//拿到刚才设置的features,作出一系列if eles判断,找出对应的resourceId
//然后调用DecorView的onResourcesLoaded,对这个layout进行inflate
···
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); //通过com.android.internal.R.id.content找到对应的mContentParent
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
//在经过一系列后续设置
···
mDecor.finishChanging();
//最后返回contentParent
return contentParent;
}

在generateLayout中,大概的流程是这样的。

  1. 先获取样式
  2. 设置样式到Window的features里
  3. 拿到features,判断对应的resourceId
  4. 通过resourceId,inflate出来一个ViewGroup,添加到mDecor中
  5. 再通过findViewById,找到刚刚inflate的ViewGroup中的 com.android.internal.R.id.content,作为mContentParent
  6. 在经过一系列设置
  7. 返回contentParent

小结

到此,我们的setContentView就已经基本走完了,剩下的就等着Activity、WindowManager、WindowManagerGlobal、ViewRootImpl去调用了,这些类的调用,涉及到了Activity的启动流程,我们会在其他笔记中详细分析这一过程。

下面会上一张整个setContentView的时序图,用来巩固一下刚才的流程。

上图为Android setContentView的时序图

系列文章

Android 视图及View绘制分析笔记之setContentView
View绘制分析笔记之onMeasure
View绘制分析笔记之onLayout
View绘制分析笔记之onDraw

1.Android 视图及View绘制分析笔记之setContentView的更多相关文章

  1. 3.View绘制分析笔记之onLayout

    上一篇文章我们了解了View的onMeasure,那么今天我们继续来学习Android View绘制三部曲的第二步,onLayout,布局. ViewRootImpl#performLayout pr ...

  2. 4.View绘制分析笔记之onDraw

    上一篇文章我们了解了View的onLayout,那么今天我们来学习Android View绘制三部曲的最后一步,onDraw,绘制. ViewRootImpl#performDraw private ...

  3. 2.View绘制分析笔记之onMeasure

    今天主要学习记录一下Android View绘制三部曲的第一步,onMeasure,测量. 起源 在Activity中,所有的View都是DecorView的子View,然后DecorView又是被V ...

  4. Android之View绘制流程开胃菜---setContentView(...)详细分析

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 1 为什么要分析setContentView方法 作为安卓开发者相信大部分都有意或者无意看过如下图示:PhoneWindow,DecorView这些 ...

  5. Android面试,View绘制流程以及invalidate()等相关方法分析

    整个View树的绘图流程是在ViewRoot.java类的performTraversals()函数展开的,该函数做的执行过程可简单概况为 根据之前设置的状态,判断是否需要重新计算视图大小(measu ...

  6. Android视图组成View

    视图组成View 创建时间: 2013-9-13 10:51 更新时间: 2013-9-13 11:04

  7. Android视图控件架构分析之View、ViewGroup

    在Android中,视图控件大致被分为两类,即ViewGroup和View,ViewGroup控件作为父控件,包含并管理着子View,通过ViewGroup和View便形成了控件树,各个ViewGou ...

  8. android事件分发源码分析—笔记

    昨天晚上从源码角度复习了一下android的事件分发机制,今天将笔记整理下放在网上.其实说复习,也是按着<android开发艺术探索>这本书作者的思路做的笔记. 目录 事件是如何从Acti ...

  9. Android GUI之View绘制流程

    在上篇文章中,我们通过跟踪源码,我们了解了Activity.Window.DecorView以及View之间的关系(查看文章:http://www.cnblogs.com/jerehedu/p/460 ...

随机推荐

  1. vim 标签页 tabnew 等的操作命令

    对于vim这个 ide来说, 单纯的用 多子窗口 来操作, 感觉还是不够的, 还要结合标签页tab pages来,才能更好的操作. 所有关于标签 的 命令行 命令都是 以 :tab开始的, 可以用ta ...

  2. Linux C++中的时间函数(转)

    http://net.pku.edu.cn/~yhf/linux_c/function/03.html   asctime(将时间和日期以字符串格式表示) 相关函数 time,ctime,gmtime ...

  3. CSS清浮动处理(Clear与BFC)

    在CSS布局中float属性经常会被用到,但使用float属性后会使其在普通流中脱离父容器,让人很苦恼 1 浮动带来布局的便利,却也带来了新问题 <!doctype html> <h ...

  4. [C/C++] C/C++延伸学习系列之STL及Boost库概述

    想要彻底搞懂C++是很难的,或许是不太现实的.但是不积硅步,无以至千里,所以抽时间来坚持学习一点,总结一点,多多锻炼几次,相信总有一天我们会变得"了解"C++. 1. C++标准库 ...

  5. jcFeather Maya 羽毛插件

    jcFeather 2.8.6 插件持续更新地址为:http://www.jerrykon.com/jcFeather.html 和 http://www.creativecrash.com/maya ...

  6. 提升PHP编程效率的20个要素

    用单引号代替双引号来包含字符串,这样做会更快一些.因为PHP会在双引号包围的字符串中搜寻变量,单引号则 不会,注意:只有echo能这么做,它是一种可以把多个字符串当作参数的“函数”(兄弟连PHP编程: ...

  7. JS打开新页面跳转

      有时候使用js进行页面跳转,想使用 a 标签中 target="_blank" 形式,跳转打开一个新的页面. 可以使用以下脚本,创建一个 a标签,然后模拟点击操作. 代码如下: ...

  8. idea开发工具破解地址

    链接失效可以使用激活码.激活码不需要联网也可以开发. idea 在注册时选择 License server ,填http://idea.iteblog.com/key.php ,然后点击激活. 激活码 ...

  9. Springmvc的跳转方式

    跳转到其他controller: 返回类型是String: return "forward:/log/home.action";  映射路径 跳转到本类Controller的某一个 ...

  10. linux shell输入重定向

    经常用(隔很久)经常忘. 备忘下:http://blog.csdn.net/ithomer/article/details/9288353