文章目录

  • 1 创建DecorView对象
  • 2 调用generateLayout(mDecor)创建mContentParent对象
  • 重点
  • 总结

关于View如何被添加到屏幕窗口上,大体有以下几步:

  • 1.创建顶层布局容器DecorView
  • 2.在顶层布局中加载基础布局ViewGroup
  • 3.将ContentView添加到基础布局中的FrameLayout

在平时加载View时,会在onCreate方法中调用setContentView()方法,传入的参数是资源布局id:

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

以上的setContentView()方法内调用了getWindow().setContentView(layoutResID)方法,我们看一下getWindow()是什么:

    public Window getWindow() {return mWindow;}

可以看到getWindow()方法返回了Window类型的mWindow。查看一下Window,它是一个抽象类,而其仅存的惟一的一个实现就是PhoneWindow:

public abstract class Window {......
}

换句话说,setContentView方法调的getWindow.setContentView,也就是调的PhoneWindow的setContentView方法。接下来我们看一下PhoneWindow类中的setContentView()方法都干了点啥?

 @Overridepublic void setContentView(int layoutResID) {// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window// decor, when theme attributes and the like are crystalized. Do not check the feature// before this happens.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;}

可以看出来,PhoneWindow类的setContentView()方法一共干了两件事:1.调用了installDecor()方法;2.通过mLayoutInflater.inflate(layoutResID, mContentParent)方法来解析传入的布局资源id。下面我们来看一下installDecor()都做了些什么:

1 创建DecorView对象

可以看到首先要创建DecorView对象。如果mDecor==null,就通过generateDecor(-1)创建mDecor对象。这个方法直接return了一个DecorView对象。下面来看一下generateDecor(-1)方法:

 protected DecorView generateDecor(int featureId) {// System process doesn't have application context and in that case we need to directly use// the context we have. Otherwise we want the application context, so we don't cling to the// activity.Context context;if (mUseDecorContext) {Context applicationContext = getContext().getApplicationContext();if (applicationContext == null) {context = getContext();} else {context = new DecorContext(applicationContext, getContext());if (mTheme != -1) {context.setTheme(mTheme);}}} else {context = getContext();}return new DecorView(context, featureId, this, getAttributes());}


刚才我们返回的,就是DecorView对象,即mDecor。而DecorView又继承了FrameLayout,换句话说,DecorView它是一个容器。

在创建好DecorView对象之后,做了一个判断:mContentParent == null,就进行后面的操作。

2 调用generateLayout(mDecor)创建mContentParent对象

创建创建mContentParent对象的过程,可以分为以下几步:

①根据系统主题的属性,设置了很多特性(如requestFeature方法和setFlags方法)
②Inflate the window decor:解析窗口view

定义了一个layoutResource值,然后根据features的不同,对layoutResource做不同的赋值/初始化操作。
③调用mDecor.onResourceLoaded(mLayoutInflater,layoutResource)

通过对layoutResource的解析创建View root对象。然后把root对象添加到了DecorView里面(通过调用addView方法)

 void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {if (mBackdropFrameRenderer != null) {loadBackgroundDrawablesIfNeeded();mBackdropFrameRenderer.onResourcesLoaded(this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),getCurrentColor(mNavigationColorViewState));}mDecorCaptionView = createDecorCaptionView(inflater);final View root = inflater.inflate(layoutResource, null);if (mDecorCaptionView != null) {if (mDecorCaptionView.getParent() == null) {addView(mDecorCaptionView,new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));}mDecorCaptionView.addView(root,new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));} else {// Put it below the color views.addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));}mContentRoot = (ViewGroup) root;initializeElevation();}

④ 通过findViewById得到容器——ViewGroup contentParent

该容器的id为固定为static final int ID_ANDROID_CONTENT=com.android.internal.R.id.contert。且这个id是主容器的资源id,是一定存在的;调用该方法,会得到contentParent

 /*** The ID that the main layout in the XML layout file should have.*/public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;

⑤ 返回contentParent

最后会返回contentParent

return contentParent;

重点

  • 1.创建了DecorView(继承了frameLayout)
  • 2.通过不同的主题、特性,解析了一个基础容器如R.layout.screen_sample,并被添加到的DecorView之上。
  • 3.通过findViewById得到了contentParent并返回。id值就是com.android.internal.R.id.contert

其中R.layout.screen_sample经过查看是线性布局,类关系如下:

  • Window是一个抽象类,体用了绘制窗口的一组通用API。
  • PhoneWindow是Window的具体继承实现类,而且该类内部包含了一个DecorView对象,该DecorView对象是所有应用窗口(Activity界面)的根View
  • DecorView是PhoneWindow的内部类,是FrameLayout的子类,是对FrameLayout的功能的修饰,是所有应用窗口的根View

在installDecor()中初始化了一个DecorView,后又调用generateLayout初始化了一个screen_sample.xml的布局(这是个线性布局,里面包裹着ViewStub表示ActionBar、FrameLayout自己的布局。其中FrameLayout的id为:@android:id/content,即com.android.internal.R.id.contert)。图示如下:
从根视图DecorView开始都是继承View的,所以视图的绘制从这个DecorView开始依次递归:(ViewGroup–>View)调用measure、layout、draw

然后这个mContentParent表示的就是FrameLayout,FrameLayout的id为:@android:id/content,即com.android.internal.R.id.contert
得到上图FrameLayout容器后,通过setContentView方法将传入的资源布局id进行处理,经过解析后添加到这个mContentParent(FrameLayout容器)中。即setContentView(R.layout.activity_main)中布局资源id代表的布局,最终被add到了screen_sample.xml这个线性布局的中了。

总结

系统会创建一个顶层布局容器DecorView,它是一个ViewGroup容器,继承FrameLayout,是PhoneWindow对象持有的1个实例,它是所有应用程序的顶层View,在系统内部进行初始化。当DecorView初始化完成之后,系统会根据应用程序的主题特性去加载一个基础容器,如NoActionBar等。不同的主题加载的基础容器也是不同的,但是始终都会有一个com.android.internal.R.id.content的容器,这是一个FrameLayout。我们通过setContentView设置的xml的布局文件,就是经过解析之后被添加到了这个FrameLayout中

View是如何被添加到屏幕窗口上的相关推荐

  1. 文件管理搜不到Android 里的文件,Android:在原始文件夹中添加文件后窗口找不到内容容器视图...

    正在获取窗口找不到内容容器视图.这个错误询问应用程序何时将检查权限Android:在原始文件夹中添加文件后窗口找不到内容容器视图 我注意到,对于穿着应用程序,我正在将穿着apk复制到原始文件夹中.它有 ...

  2. 右键点“工作空间”窗口内空白部分,在弹出的菜单上勾选“Docking View / 停靠式”。然后双击程序窗口的窗棱,就是最上面那条蓝色边框

    VC++6.0的界面如何恢复 这里的每个界面都是突出的,如何恢复成下面这个图的样子?请各位指点一下,本人将感激不尽!...展开 右键点"工作空间"窗口内空白部分,在弹出的菜单上勾选 ...

  3. 汇编实验:屏幕窗口程序(代码有较为详细的注释)

    实验3.4 屏幕窗口程序 1.题目:屏幕窗口程序 2.实验要求: 在屏幕上开出三个窗口,它们的行列坐标按照教材要求.光标首先定位在右窗口最下面一行的行首(15,50),如从键盘输入字符,则显示在右窗口 ...

  4. 汇编实验:屏幕窗口程序

    实验3.4  屏幕窗口程序 1.题目:屏幕窗口程序 2.实验要求: 在屏幕上开出三个窗口,它们的行列坐标按照教材要求.光标首先定位在右窗口最下面一行的行首(15,50),如从键盘输入字符,则显示在右窗 ...

  5. html页面添加遮罩层,在浏览器窗口上添加遮罩层的方法

    如何在浏览器窗口上添加一个遮罩层 背景 在web2.0中,页面弹窗是一个很常见的交互方式,这样既可以避免不必要的页面跳转,也可以改进界面的布局和可交互性. 但是,浏览器原生的弹窗函数(alert, c ...

  6. 判断View滑入或滑出屏幕可见区

    今天在用今日头条app的时候发现了一个现象,比如你正在看视频(未全屏),此时上下滑动使得视频滑出屏幕可见区,那刚才正在播放的视频就自动暂停了. 这里思考下,肯定有某种方式能够知道View是否在屏幕可见 ...

  7. Android源码编译:任意界面屏幕边缘上滑弹出快捷操作栏【一键加速、开关控制】

    <The Fucking Source Code> 注:以下均为android源码Framework层修改. 设计实现在任意界面从屏幕边缘上滑弹出快捷操作栏,包括亮度调节.正在后台运行的程 ...

  8. Supporting Multiple Screens 翻译 支持各种屏幕(上)

    Supporting Multiple Screens 支持各种各样的屏幕尺寸.屏幕密度 Android runs on a variety of devices that offer differe ...

  9. MFC中的控件是怎么实现的呢?一个按钮就是一个窗口?还是所有按钮画在一个分层窗口上再叠

    请教一下MFC中的控件是怎么实现的呢(通过调用什么样的API,传递什么参数)?一个按钮就是一个窗口?还是所有按钮画在一个分层窗口上再叠在主窗口上?还是直接在主窗口上画图? 我想到的是: JavaScr ...

最新文章

  1. button hover逐渐变色_两当水库界桩@产品长久不变色 - 两当安全防护
  2. 解决wordpress无法离线发布(远程发布)的故障
  3. ORA-00119,ORA-00132 错误处理
  4. 生产者消费者之阻塞队列版本
  5. 单片机万年历阴阳历c语言,单片机 阴历阳历c算法 万年历
  6. 李宏毅机器学习Regression
  7. 浅析javaIO的原理过程
  8. 什么是云计算架构和组件
  9. 南开介教授好嘛是“段子手”
  10. 创新实践部第一次培训---算法入门
  11. C#使用消息队列(MSMQ)
  12. latex算法框三线加粗
  13. Python—标准遗传算法求函数最大值代码实现
  14. tplink 虚拟服务器没有https,tplink虚拟服务器设置
  15. centos下zip压缩解压缩命令
  16. 从Android代码中来记忆23种设计模式
  17. 机器学习/深度学习/图机器学习 学习小结
  18. 【物理应用】基于Matlab实现LBM-D2Q9模型粗糙界面流动
  19. 关于如何办好培训的感想
  20. 提取Seurat格式单细胞metadata和counts 新建seurat object

热门文章

  1. JXJJOI2018_T2_tank
  2. mysql-atlas安装及使用教程
  3. J0ker的CISSP之路:复习Access Control(10)
  4. jQuery中读取json文件示例代码
  5. gecco 1.1.0稳定版发布,易用的轻量化爬虫
  6. 报错-Unknown class in Interface Builder file
  7. Qt5官方demo分析集29——Extending QML - Property Value Source Example
  8. MyBatis实现与插件开发
  9. Testlink在linux上安装遇到的问题
  10. emwin之基于某个事件或标志创建某个界面的一种方法