热门标签 | 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]》



推荐阅读
  • Gradle 是 Android Studio 中默认的构建工具,了解其基本配置对于开发效率的提升至关重要。本文将详细介绍如何在 Gradle 中定义和使用共享变量,以确保项目的一致性和可维护性。 ... [详细]
  • 深入解析Unity3D游戏开发中的音频播放技术
    在游戏开发中,音频播放是提升玩家沉浸感的关键因素之一。本文将探讨如何在Unity3D中高效地管理和播放不同类型的游戏音频,包括背景音乐和效果音效,并介绍实现这些功能的具体步骤。 ... [详细]
  • 本文详细介绍了JQuery Mobile框架中特有的事件和方法,帮助开发者更好地理解和应用这些特性,提升移动Web开发的效率。 ... [详细]
  • 在Android中实现黑客帝国风格的数字雨效果
    本文将详细介绍如何在Android平台上利用自定义View实现类似《黑客帝国》中的数字雨效果。通过实例代码,我们将探讨如何设置文字颜色、大小,以及如何控制数字下落的速度和间隔。 ... [详细]
  • JavaScript 跨域解决方案详解
    本文详细介绍了JavaScript在不同域之间进行数据传输或通信的技术,包括使用JSONP、修改document.domain、利用window.name以及HTML5的postMessage方法等跨域解决方案。 ... [详细]
  • 在尝试加载支持推送通知的iOS应用程序的Ad Hoc构建时,遇到了‘no valid aps-environment entitlement found for application’的错误提示。本文将探讨此错误的原因及多种可能的解决方案。 ... [详细]
  • 本文介绍了如何通过C#语言调用动态链接库(DLL)中的函数来实现IC卡的基本操作,包括初始化设备、设置密码模式、获取设备状态等,并详细展示了将TextBox中的数据写入IC卡的具体实现方法。 ... [详细]
  • 实现Win10与Linux服务器的SSH无密码登录
    本文介绍了如何在Windows 10环境下使用Git工具,通过配置SSH密钥对,实现与Linux服务器的无密码登录。主要步骤包括生成本地公钥、上传至服务器以及配置服务器端的信任关系。 ... [详细]
  • 本文探讨了使用Python实现监控信息收集的方法,涵盖从基础的日志记录到复杂的系统运维解决方案,旨在帮助开发者和运维人员提升工作效率。 ... [详细]
  • Eclipse 开发环境配置指南:从下载到免安装配置
    Eclipse 是一个广泛使用的开源Java开发环境,掌握其正确的安装与配置方法对于初学者至关重要。本文将详细介绍如何下载、安装及配置Eclipse,确保您的开发环境搭建顺利。 ... [详细]
  • 本文将详细介绍如何在Windows 10操作系统中轻松设置本地连接,包括基本步骤和常见问题的解决方案,帮助用户快速掌握操作技巧。 ... [详细]
  • 尽管在WPF中工作了一段时间,但在菜单控件的样式设置上遇到了一些基础问题,特别是关于如何正确配置前景色和背景色。 ... [详细]
  • OpenCV中的霍夫圆检测技术解析
    本文详细介绍了如何使用OpenCV库中的HoughCircles函数实现霍夫圆检测,并提供了具体的代码示例及参数解释。 ... [详细]
  • 本文探讨了如何通过优化 DOM 操作来提升 JavaScript 的性能,包括使用 `createElement` 函数、动画元素、理解重绘事件及处理鼠标滚动事件等关键主题。 ... [详细]
  • 本文详细介绍了如何在Oracle VM VirtualBox中实现主机与虚拟机之间的数据交换,包括安装Guest Additions增强功能,以及如何利用这些功能进行文件传输、屏幕调整等操作。 ... [详细]
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社区 版权所有