热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

android在类中将布局文件指定为,Android中将布局文件/View添加至窗口过程分析

本文主要内容是讲解一个视图View或者一个ViewGroup对象是如何添加至应用程序窗口中的。下文中提到的窗口可泛指我们能看到的界面,包括一个Activity呈现的界

本文主要内容是讲解一个视图View或者一个ViewGroup对象是如何添加至应用程序窗口中的。

下文中提到的窗口可泛指我们能看到的界面,包括一个Activity呈现的界面(我们可以将之理解为应用程序窗口),一个Dialog,

一个Toast,一个Menu菜单等。

首先对相关类的作用进行一下简单介绍:

Window 类   位于 /frameworks/base/core/java/android/view/Window.java

说明:该类是一个抽象类,提供了绘制窗口的一组通用API。可以将之理解为一个载体,各种View在这个载体上显示。

源文件(部分)如下:publicabstractclassWindow {

//...

//指定Activity窗口的风格类型

publicstaticfinalintFEATURE_NO_TITLE =1;

publicstaticfinalintFEATURE_INDETERMINATE_PROGRESS =5;

//设置布局文件

publicabstractvoidsetContentView(intlayoutResID);

publicabstractvoidsetContentView(View view);

//请求指定Activity窗口的风格类型

publicbooleanrequestFeature(intfeatureId) {

finalintflag &#61;1<

mFeatures |&#61; flag;

mLocalFeatures |&#61; mContainer !&#61; null? (flag&~mContainer.mFeatures) : flag;

return(mFeatures&flag) !&#61;0;

}

//...

}

PhoneWindow类  位于/frameworks/policies/base/phone/com/android/internal/policy/impl/PhoneWindow.java

说明&#xff1a; 该类继承于Window类&#xff0c;是Window类的具体实现&#xff0c;即我们可以通过该类具体去绘制窗口。并且&#xff0c;该类内部包含了

一个DecorView对象&#xff0c;该DectorView对象是所有应用窗口(Activity界面)的根View。 简而言之&#xff0c;PhoneWindow类是

把一个FrameLayout类即DecorView对象进行一定的包装&#xff0c;将它作为应用窗口的根View&#xff0c;并提供一组通用的窗口操作

接口。

源文件(部分)如下&#xff1a;publicclassPhoneWindowextendsWindowimplementsMenuBuilder.Callback {

//...

// This is the top-level view of the window, containing the window decor.

privateDecorView mDecor;//该对象是所有应用窗口的根视图 &#xff0c; 是FrameLayout的子类

//该对象是Activity布局文件的父视图&#xff0c;一般来说是一个FrameLayout型的ViewGroup

// 同时也是DecorView对象的一个子视图

// This is the view in which the window contents are placed. It is either

// mDecor itself, or a child of mDecor where the contents go.

privateViewGroup mContentParent;

//设置标题

&#64;Override

publicvoidsetTitle(CharSequence title) {

if(mTitleView !&#61;null) {

mTitleView.setText(title);

}

mTitle &#61; title;

}

//设置背景图片

&#64;Override

publicfinalvoidsetBackgroundDrawable(Drawable drawable) {

if(drawable !&#61; mBackgroundDrawable || mBackgroundResource !&#61;0) {

mBackgroundResource &#61; 0;

mBackgroundDrawable &#61; drawable;

if(mDecor !&#61;null) {

mDecor.setWindowBackground(drawable);

}

}

}

//...

}

DecorView类    该类是PhoneWindow类的内部类

说明&#xff1a; 该类是一个FrameLayout的子类&#xff0c;并且是PhoneWindow的子类&#xff0c;该类就是对普通的FrameLayout进行功能的扩展&#xff0c;

更确切点可以说是修饰(Decor的英文全称是Decoration&#xff0c;即“修饰”的意思)&#xff0c;比如说添加TitleBar(标题栏)&#xff0c;以及

TitleBar上的滚动条等 。最重要的一点是&#xff0c;它是所有应用窗口的根View 。

如下所示 &#xff1a;

0_13281077452L6c.gif             

0_1328107759000D.gif

DecorView 根视图结构                                                          DecorView 根视图形式

源文件(部分)如下&#xff1a;privatefinalclassDecorViewextendsFrameLayout {

//...

//触摸事件处理

&#64;Override

publicbooleanonTouchEvent(MotionEvent event) {

returnonInterceptTouchEvent(event);

}

//...

}

打个不恰当比喻吧&#xff0c;Window类相当于一幅画(抽象概念&#xff0c;什么画我们未知) &#xff0c;PhoneWindow为一副齐白石先生的山水画

(具体概念&#xff0c;我们知道了是谁的、什么性质的画)&#xff0c;DecorView则为该山水画的具体内容(有山、有水、有树&#xff0c;各种界面)。

DecorView呈现在PhoneWindow上。

当系统(一般是ActivityManagerService)配置好启动一个Activity的相关参数(包括Activity对象和Window对象信息)后&#xff0c;

就会回调Activity的onCreate()方法&#xff0c;在其中我们通过设置setContentView()方法类设置该Activity的显示界面&#xff0c;整个调用链

由此铺垫开来。setContentView()的三个构造方法调用流程本质上是一样的&#xff0c;我们就分析setContentView(intresId)方法。

Step 1、Activity.setContentView(intresId)   该方法在Activity类中

该方法只是简单的回调Window对象&#xff0c;具体为PhoneWindow对象的setContentView()方法实现 。

publicvoidsetContentView(intlayoutResID) {

getWindow().setContentView(layoutResID);

}

publicWindow getWindow() {

returnmWindow;//Window对象&#xff0c;本质上是一个PhoneWindow对象

}

Step 2、PhoneWindow.setContentView()    该方法在PhoneWindow类中

&#64;Override

publicvoidsetContentView(intlayoutResID) {

//是否是第一次调用setContentView方法&#xff0c; 如果是第一次调用&#xff0c;则mDecor和mContentParent对象都为空

if(mContentParent &#61;&#61;null) {

installDecor();

} else{

mContentParent.removeAllViews();

}

mLayoutInflater.inflate(layoutResID, mContentParent);

finalCallback cb &#61; getCallback();

if(cb !&#61;null) {

cb.onContentChanged();

}

}

该方法根据首先判断是否已经由setContentView()了获取mContentParent即View对象&#xff0c; 即是否是第一次调用该

PhoneWindow对象setContentView()方法。如果是第一次调用&#xff0c;则调用installDecor()方法&#xff0c;否则&#xff0c;移除该mContentParent内

所有的所有子View。最后将我们的资源文件通过LayoutInflater对象转换为View树&#xff0c;并且添加至mContentParent视图中。

PS&#xff1a;因此&#xff0c;在应用程序里&#xff0c;我们可以多次调用setContentView()来显示我们的界面。

Step 3、 PhoneWindow. installDecor()    该方法在PhoneWindow类中

privatevoidinstallDecor() {

if(mDecor &#61;&#61;null) {

//mDecor为空&#xff0c;则创建一个Decor对象

mDecor &#61; generateDecor();

mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

mDecor.setIsRootNamespace(true);

}

if(mContentParent &#61;&#61;null) {

//generateLayout()方法会根据窗口的风格修饰&#xff0c;选择对应的修饰布局文件

//并且将id为content(android:id&#61;"&#64;&#43;id/content")的FrameLayout赋值给mContentParent

mContentParent &#61; generateLayout(mDecor);

//...

}

首先、该方法首先判断mDecor对象是否为空&#xff0c;如果不为空&#xff0c;则调用generateDecor()创建一个DecorView(该类是

FrameLayout子类&#xff0c;即一个ViewGroup视图) &#xff1b;

generateDecor()方法原型为:protectedDecorView generateDecor() {

returnnewDecorView(getContext(), -1);

}

其次、继续判断mContentParent对象是否为空&#xff0c;如果不为空&#xff0c;则调用generateLayout()方法去创建mContentParent对象。

generateLayout()方法如下&#xff1a;

protectedViewGroup generateLayout(DecorView decor) {

// Apply data from current theme.

//...1、根据requestFreature()和Activity节点的android:theme&#61;"" 设置好 features值

//2 根据设定好的 features值&#xff0c;即特定风格属性&#xff0c;选择不同的窗口修饰布局文件

intlayoutResource;//窗口修饰布局文件

intfeatures &#61; getLocalFeatures();

// System.out.println("Features: 0x" &#43; Integer.toHexString(features));

if((features & ((1<

if(mIsFloating) {

layoutResource &#61; com.android.internal.R.layout.dialog_title_icons;

} else{

layoutResource &#61; com.android.internal.R.layout.screen_title_icons;

}

// System.out.println("Title Icons!");

} elseif((features & ((1<

// Special case for a window with only a progress bar (and title).

// XXX Need to have a no-title version of embedded windows.

layoutResource &#61; com.android.internal.R.layout.screen_progress;

// System.out.println("Progress!");

}

//...

//3 选定了窗口修饰布局文件 &#xff0c;添加至DecorView对象里&#xff0c;并且指定mcontentParent值

View in &#61; mLayoutInflater.inflate(layoutResource, null);

decor.addView(in, newViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));

ViewGroup contentParent &#61; (ViewGroup)findViewById(ID_ANDROID_CONTENT);

if(contentParent &#61;&#61;null) {

thrownewRuntimeException("Window couldn&#39;t find content container view");

}

if((features & (1<

ProgressBar progress &#61; getCircularProgressBar(false);

if(progress !&#61;null) {

progress.setIndeterminate(true);

}

}

//...

returncontentParent;

}

该方法会做如下事情&#xff1a;

1、根据窗口的风格修饰类型为该窗口选择不同的窗口布局文件(根视图)。这些窗口修饰布局文件指定一个用来存放

Activity自定义布局文件的ViewGroup视图&#xff0c;一般为FrameLayout 其id 为&#xff1a; android:id&#61;"&#64;android:id/content"。

例如窗口修饰类型包括FullScreen(全屏)、NoTitleBar(不含标题栏)等。选定窗口修饰类型有两种&#xff1a;

①、指定requestFeature()指定窗口修饰符&#xff0c;PhoneWindow对象调用getLocalFeature()方法获取值&#xff1b;

②、为我们的Activity配置相应属性&#xff0c;即android&#xff1a;theme&#61;“”&#xff0c;PhoneWindow对象调用getWindowStyle()方法

获取值。

举例如下&#xff0c;隐藏标题栏有如下方法&#xff1a;requestWindowFeature(Window.FEATURE_NO_TITLE);

或者 为Activity配置xml属性&#xff1a;android:theme&#61;”&#64;android:style/Theme.NoTitleBar”。

PS&#xff1a;因此&#xff0c;在Activity中必须在setContentView之前调用requestFeature()方法。

确定好窗口风格之后&#xff0c;选定该风格对应的布局文件&#xff0c;这些布局文件位于 frameworks/base/core/res/layout/  &#xff0c;

典型的窗口布局文件有&#xff1a;

R.layout.dialog_titile_icons                          R.layout.screen_title_icons

R.layout.screen_progress                             R.layout.dialog_custom_title

R.layout.dialog_title

R.layout.screen_title         // 最常用的Activity窗口修饰布局文件

R.layout.screen_simple    //全屏的Activity窗口布局文件

分析Activity最常用的一种窗口布局文件&#xff0c;R.layout.screen_title  &#xff1a;

android:orientation&#61;"vertical"

android:fitsSystemWindows&#61;"true">

android:layout_width&#61;"match_parent"

android:layout_height&#61;"?android:attr/windowTitleSize"

style&#61;"?android:attr/windowTitleBackgroundStyle">

style&#61;"?android:attr/windowTitleStyle"

android:background&#61;"&#64;null"

android:fadingEdge&#61;"horizontal"

android:gravity&#61;"center_vertical"

android:layout_width&#61;"match_parent"

android:layout_height&#61;"match_parent"/>

android:layout_width&#61;"match_parent"

android:layout_height&#61;"0dip"

android:layout_weight&#61;"1"

android:foregroundGravity&#61;"fill_horizontal|top"

android:foreground&#61;"?android:attr/windowContentOverlay"/>

该布局文件很简单&#xff0c;一个LinearLayout下包含了两个子FrameLayout视图&#xff0c;第一个FrameLayout用来显示标题栏(TitleBar)&#xff0c;

该TextView 视图id为title(android:id&#61;"&#64;android:id/title")&#xff1b;第二个FrameLayout用来显示我们Activity的布局文件的父视图&#xff0c;

该FrameLayoutid为content(android:id&#61;"&#64;android:id/content") 。

全屏的窗口布局文件 R.layout.screen_simple:

enabled.

-->

android:id&#61;"&#64;android:id/content"

android:fitsSystemWindows&#61;"true"

android:foregroundInsidePadding&#61;"false"

android:foregroundGravity&#61;"fill_horizontal|top"

android:foreground&#61;"?android:attr/windowContentOverlay"/>

该布局文件只有一个FrameLayout&#xff0c;用来显示我们Activity的布局文件&#xff0c;该FrameLayoutid为

android:id&#61;"&#64;android:id/content"

2、前面一步我们确定窗口修饰布局文件后&#xff0c;mDecor做为根视图将该窗口布局对应的视图添加进去&#xff0c;并且获取id为content

的View&#xff0c;将其赋值给mContentParent对象&#xff0c;即我们前面中提到的第二个FrameLayout。

At Last、产生了mDecor和mContentParent对象后&#xff0c;就将我们的Activity布局文件直接添加至mContentParent父视图中即可。

我们再次回到 Step 2 中PhoneWindow.setContentView()      该方法在PhoneWindow类中

&#64;Override

publicvoidsetContentView(intlayoutResID) {

if(mContentParent &#61;&#61;null) {

installDecor();

} else{

mContentParent.removeAllViews();

}

mLayoutInflater.inflate(layoutResID, mContentParent);

finalCallback cb &#61; getCallback();

if(cb !&#61;null) {

cb.onContentChanged();

}

}

整个过程主要是如何把Activity的布局文件添加至窗口里&#xff0c;上面的过程可以概括为&#xff1a;

1、创建一个DecorView对象&#xff0c;该对象将作为整个应用窗口的根视图

2、创建不同的窗口修饰布局文件&#xff0c;并且获取Activity的布局文件该存放的地方&#xff0c;由该窗口修饰布局文件内id为content的

FrameLayout指定 。

3、将Activity的布局文件添加至id为content的FrameLayout内。

最后&#xff0c;当AMS(ActivityManagerService)准备resume一个Activity时&#xff0c;会回调该Activity的handleResumeActivity()方法&#xff0c;

该方法会调用Activity的makeVisible方法 ,显示我们刚才创建的mDecor 视图族。

//系统resume一个Activity时&#xff0c;调用此方法

finalvoidhandleResumeActivity(IBinder token,booleanclearHide,booleanisForward) {

ActivityRecord r &#61; performResumeActivity(token, clearHide);

//...

if(r.activity.mVisibleFromClient) {

r.activity.makeVisible();

}

}

handleResumeActivity()方法原型如下&#xff1a; 位于ActivityThread类中voidmakeVisible() {

if(!mWindowAdded) {

ViewManager wm &#61; getWindowManager();   // 获取WindowManager对象

wm.addView(mDecor, getWindow().getAttributes());

mWindowAdded &#61; true;

}

mDecor.setVisibility(View.VISIBLE); //使其处于显示状况

}

接下来就是&#xff0c;如何把我们已经创建好的窗口通知给WindowManagerService &#xff0c;以便它能够把这个窗口显示在屏幕上。

关于这方面内容大家可以去看邓凡平老师的这篇博客《Android深入浅出之Surface[1]》



推荐阅读
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 如何搭建Java开发环境并开发WinCE项目
    本文介绍了如何搭建Java开发环境并开发WinCE项目,包括搭建开发环境的步骤和获取SDK的几种方式。同时还解答了一些关于WinCE开发的常见问题。通过阅读本文,您将了解如何使用Java进行嵌入式开发,并能够顺利开发WinCE应用程序。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 树莓派语音控制的配置方法和步骤
    本文介绍了在树莓派上实现语音控制的配置方法和步骤。首先感谢博主Eoman的帮助,文章参考了他的内容。树莓派的配置需要通过sudo raspi-config进行,然后使用Eoman的控制方法,即安装wiringPi库并编写控制引脚的脚本。具体的安装步骤和脚本编写方法在文章中详细介绍。 ... [详细]
  • 解决文件名过长下载失败问题的jQuery方案
    本文介绍了使用jQuery解决文件名过长导致下载失败的问题。原方案中存在文件名部分丢失的问题,通过动态生成隐藏域表单并提交的方式来解决。详细的解决方案和代码示例在文章中给出。 ... [详细]
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • GreenDAO快速入门
    前言之前在自己做项目的时候,用到了GreenDAO数据库,其实对于数据库辅助工具库从OrmLite,到litePal再到GreenDAO,总是在不停的切换,但是没有真正去了解他们的 ... [详细]
author-avatar
梦幻小螳螂
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有