DecorView

开发中,通常都是在onCreate()中调用setContentView(R.layout.custom_layout)来实现想要的页面布局。页面都是依附在窗口之上的,而DecorView即是窗口最顶层的视图。Android frameworks中,与窗口视图处理相关的类,主要是Window及其实现类PhoneWindow

public class PhoneWindow extends Window implements MenuBuilder.Callback {
  
  //...   //窗口顶层View
private DecorView mDecor;   //所有自定义View的根View, id="@android:id/content"
private ViewGroup mContentParent;   //... }

DecorView其实是PhoneWindow中的一个内部类,本质上也是一个View,其只是扩展了FrameLayout的实现

private final class DecorView extends FrameLayout implements RootViewSurfaceTaker

添加至窗口流程

  • Activity中调用setContentView(R.layout.custom_layout), 具体实现为PhoneWindow中的同名方法
public void setContentView(int layoutResID) {
  
  //getWindow()获取的即是PhoneWindow对象
  getWindow().setContentView(layoutResID);
}

看一下window类

public abstract class Window {
//...
//指定Activity窗口的风格类型
public static final int FEATURE_NO_TITLE = 1;
public static final int FEATURE_INDETERMINATE_PROGRESS = 5; //设置布局文件
public abstract void setContentView(int layoutResID); public abstract void setContentView(View view); //请求指定Activity窗口的风格类型
public boolean requestFeature(int featureId) {
final int flag = 1<<featureId;
mFeatures |= flag;
mLocalFeatures |= mContainer != null ? (flag&~mContainer.mFeatures) : flag;
return (mFeatures&flag) != 0;
}
//...
}
  • PhoneWindow执行setContentView(int layoutResource)

PhoneWindow该类继承于Window类,是Window类的具体实现,即我们可以通过该类具体去绘制窗口。并且,该类内部包含了一个DecorView对象,该DectorView对象是所有应用窗口(Activity界面)的根View。 简而言之,PhoneWindow类是把一个FrameLayout类即DecorView对象进行一定的包装,将它作为应用窗口的根View,并提供一组通用的窗口操作接口。

public void setContentView(int layoutResID) {
  //初始,mContentParent为空
  if (mContentParent == null) {
    installDecor();
  } else {
    mContentParent.removeAllViews();
  }
  //inflate自定义layout, 并将mContentParent作为其根视图
  mLayoutInflater.inflate(layoutResID, mContentParent);   //...
}

该方法根据首先判断是否已经由setContentView()了获取mContentParent即View对象, 即是否是第一次调用该PhoneWindow对象setContentView()方法。如果是第一次调用,则调用installDecor()方法,否则,移除该mContentParent内所有的所有子View。最后将我们的资源文件通过LayoutInflater对象转换为View树,并且添加至mContentParent视图中(在应用程序里,我们可以多次调用setContentView()来显示我们的界面。)。

  • PhoneWindow.installDecor()
private void installDecor() {
  if (mDecor == null) {
     //new一个DecorView
    mDecor = generateDecor();
    mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
    mDecor.setIsRootNamespace(true);
    if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
      mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
    }
  }
  if (mContentParent == null) {
     //这一步会设置窗口的修饰文件,并将id为ID_ANDROID_CONTENT的view find出来作为返回值赋值给mContentParent
    mContentParent = generateLayout(mDecor); //...
}
  • PhoneWindow.generateLayout(DecorView decor)
protected ViewGroup generateLayout(DecorView decor) {
  //1,获取<Application android:theme=""/>, <Activity/>节点指定的themes或者代码requestWindowFeature()中指定的Features, 并设置
  TypedArray a = getWindowStyle();
  //...
  
  //2,获取窗口Features, 设置相应的修饰布局文件,这些xml文件位于frameworks/base/core/res/res/layout下
  int layoutResource;
  int features = getLocalFeatures();
  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;
  }
  removeFeature(FEATURE_ACTION_BAR);
  } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0 && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
  layoutResource = com.android.internal.R.layout.screen_progress;
  //...
  
  mDecor.startChanging();
  //3, 将上面选定的布局文件inflate为View树,添加到decorView中
  View in = mLayoutInflater.inflate(layoutResource, null);
  decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
  //将窗口修饰布局文件中id="@android:id/content"的View赋值给mContentParent, 后续自定义的view/layout都将是其子View
  ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
  if (contentParent == null) {
    throw new RuntimeException("Window couldn't find content container view");
  }
  //...
}

该方法会做如下事情:

根据窗口的风格修饰类型为该窗口选择不同的窗口布局文件(根视图)。这些窗口修饰布局文件指定一个用来存放Activity自定义布局文件的ViewGroup视图,一般为FrameLayout 其id 为: android:id="@android:id/content"。

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

①指定requestFeature()指定窗口修饰符,PhoneWindow对象调用getLocalFeature()方法获取值;

②为我们的Activity配置相应属性,即android:theme=“”,PhoneWindow对象调用getWindowStyle()方法获取值。

举例如下,隐藏标题栏有如下方法:

requestWindowFeature(Window.FEATURE_NO_TITLE);

或者为Activity配置xml属性:

android:theme="@android:style/Theme.NoTitleBar"

因此,在Activity中必须在setContentView之前调用requestFeature()方法。

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

典型的窗口布局文件有:

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窗口布局文件
  • 最后页面中设置的自定义layout会被添加到mContentParent中
mLayoutInflater.inflate(layoutResID, mContentParent);

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

  1. 创建一个DecorView对象,该对象将作为整个应用窗口的根视图
  2. 创建不同的窗口修饰布局文件,并且获取Activity的布局文件该存放的地方,由该窗口修饰布局文件内id为content的FrameLayout指定 。
  3. 将Activity的布局文件添加至id为content的FrameLayout内。

最后,当AMS(ActivityManagerService)准备resume一个Activity时,会回调该Activity的handleResumeActivity()方法,该方法会调用Activity的makeVisible方法 ,显示我们刚才创建的mDecor 视图族。

//系统resume一个Activity时,调用此方法
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {
ActivityRecord r = performResumeActivity(token, clearHide);
//...
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
}
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager(); // 获取WindowManager对象
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE); //使其处于显示状况
}

布局层次结构

干货

@Override
protected void onCreate(Bundle savedInstanceState) {
  //设置窗口无标题栏
  requestWindowFeature(Window.FEATURE_NO_TITLE);
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_decor);
}

activity_decor.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".DecorActivity"> <TextView
android:text="@string/hello_world"
android:layout_width="wrap_content"
android:layout_height="wrap_content" /> <TextView
android:text="@string/hello_world"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"/> </RelativeLayout>
 

onCreate()中设置的Window.FEATURE_NO_TITLE对应的窗口修饰布局文件为screen_simple.xml, 源码如下

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<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/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foregroundInsidePadding="false"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

源码中id为"@android:id/content"的FrameLayout就是内容区域,在整个流程中,其会赋值给PhoneWindow类中的属性mContentParent, 运行应用后,使用SDK提供的hierarchyviewer工具查看页面的ViewTree结构,可以看到结构如下:

我是天王盖地虎的分割线

参考:http://www.cnblogs.com/yogin/p/4061050.html

Android -- DecorView的更多相关文章

  1. android DecorView深入理解

    开发中,通常都是在onCreate()中调用setContentView(R.layout.custom_layout)来实现想要的页面布局.页面都是依附在窗口之上的,而DecorView即是窗口最顶 ...

  2. 理解Android DecorView

    一.DecorView为整个Window界面的最顶层View. 二.DecorView只有一个子元素为LinearLayout.代表整个Window界面,包含通知栏,标题栏,内容显示栏三块区域. 三. ...

  3. Android DecorView浅析

    摘要 一.DecorView为整个Window界面的最顶层View. 二.DecorView只有一个子元素为LinearLayout.代表整个Window界面,包含通知栏,标题栏,内容显示栏三块区域. ...

  4. Android DecorView 与 Activity 绑定原理分析

    一年多以前,曾经以为自己对 View 的绘制已经有了解了,事后发现也只是懂了些皮毛而已.经过一年多的实战,Android 和 Java 基础都有了提升,时机成熟了,是时候该去总结 View 的绘制流程 ...

  5. android decorView详解

    摘要 一.DecorView为整个Window界面的最顶层View. 二.DecorView只有一个子元素为LinearLayout.代表整个Window界面,包含通知栏,标题栏,内容显示栏三块区域. ...

  6. View学习(一)-DecorView,measureSpec与LayoutParams

    这段时间在学习android中view的工作原理与自定义View的相关内容,所以未来这这几篇博客都总结一下相关的知识吧. 首先我们要了解和熟悉两个概念,DecorView 与 MeasureSpec. ...

  7. Android 如何动态添加 View 并显示在指定位置。

    引子 最近,在做产品的需求的时候,遇到 PM 要求在某个按钮上添加一个新手引导动画,引导用户去点击.作为 RD,我哗啦啦的就写好相关逻辑了.自测完成后,提测,PM Review 效果. 看完后,PM ...

  8. MonkenRunner通过HierarchyViewer定位控制的方法和建议(Appium/UIAutomator/Robotium侣)

    1. 背景 正在使用MonkeyRunner当我们经常使用Chimchat下面HierarchyViewer模块获得目标控制的一些信息,以协助我们测试.但在MonkeyRunner官方的说法是没有看到 ...

  9. MonkenRunner通过HierarchyViewer定位控件的方法和建议(Appium/UIAutomator/Robotium姊妹篇)

    1. 背景 在使用MonkeyRunner的时候我们经常会用到Chimchat下面的HierarchyViewer模块来获取目标控件的一些信息来辅助我们测试,但在MonkeyRunner的官网上是没有 ...

随机推荐

  1. 七层负载(Application Gateway)+四层负载(LB)

    上次有个电商客户需要搭建如架构. 192.168.1.100/url1(请求url)——>Node1:10.0.0.4.10.0.0.5(服务器IP) 192.168.1.100/url2(请求 ...

  2. c# 实现获取汉字十六进制Unicode编码字符串

    1.  汉字转十六进制UNICODE编码字符串 /// <summary>        /// ////        /// </summary>        /// & ...

  3. STM32F4 How do you generate complementary PWM Outputs?

    How do you generate complementary PWM Outputs? I would like to generate complementary PWM Outputs wi ...

  4. [Go] 编码规范

    gofmt 大部分的格式问题可以通过 gofmt 解决,gofmt 自动格式化代码,保证所有的Go代码与官方推荐的格式保持一致,于是所有格式有关问题,都以 gofmt 的结果为准. 注释 在编码阶段应 ...

  5. 微信emoji表情编码 、MySQL 存储 emoji 表情符号字符集

    相关资料 微信emoji表情编码 微信用户名显示「emoji表情」 PHP处理微信中带Emoji表情的消息发送和接收(Unicode字符转码编码) MySQL 存储emoji表情 MySQL 存储 e ...

  6. 阅读Linux内核源码时建立tags索引

    比如在阅读arm架构的Linux内核代码时想建立arm架构相关的索引,可以使用下面的命令: make ARCH=arm tags

  7. intellij idea 无法启动或调试 spring-boot

    解决方案一: 原因是因为Working directory没有指定, 并且运行前要手动执行mvn clean install命令才可以.所以导致错误了.希望大家不要犯类似错误. 解决方式二: 看看你的 ...

  8. IOS判断用户的网络类型(2/3/4G、wifi)

    直接贴代码吧,ios7之后是获取的较为准确,7以下我拿iphone5测试的是无法区分3g/2g.连iphone4都能升到7.1.4,而且目前主流的设备7以下的系统已经很少了,这个方案尽管不太完美,但影 ...

  9. Java_并发线程_Semaphore、CountDownLatch、CyclicBarrier、Exchanger

    1.Semaphore 信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施, 它负责协调各个线程, 以保证它们可以正确.合理的使用公共资源. Semaphore当前在多线程 ...

  10. Android Activity启动流程源码全解析(2)

    接上之前的分析 ++Android Activity启动流程源码全解析(1)++ 1.正在运行的Activity调用startPausingLocked 一个一个分析,先来看看startPausing ...