Android -- DecorView

简介:



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

添加至窗口流程

1

  • 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); //使其处于显示状况  
}
复制代码

布局层次结构

2

干货

复制代码
@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结构,可以看到结构如下:

3

我是天王盖地虎的分割线

 

 




本文转自我爱物联网博客园博客,原文链接:http://www.cnblogs.com/yydcdut/p/4472227.html,如需转载请自行联系原作者

相关文章
|
Android开发
android decorView详解
摘要 一、DecorView为整个Window界面的最顶层View。 二、DecorView只有一个子元素为LinearLayout。代表整个Window界面,包含通知栏,标题栏,内容显示栏三块区域。 三、LinearLayout里有两个FrameLayout子元素。 (20)为标题栏显示界面。只有一个TextView显示应用 (请发邮件到 freeget.one@g
1786 0
|
21天前
|
Java Android开发
Android 开发获取通知栏权限时会出现两个应用图标
Android 开发获取通知栏权限时会出现两个应用图标
12 0
|
1月前
|
XML 缓存 Android开发
Android开发,使用kotlin学习多媒体功能(详细)
Android开发,使用kotlin学习多媒体功能(详细)
96 0
|
1月前
|
设计模式 人工智能 开发工具
安卓应用开发:构建未来移动体验
【2月更文挑战第17天】 随着智能手机的普及和移动互联网技术的不断进步,安卓应用开发已成为一个热门领域。本文将深入探讨安卓平台的应用开发流程、关键技术以及未来发展趋势。通过分析安卓系统的架构、开发工具和框架,本文旨在为开发者提供全面的技术指导,帮助他们构建高效、创新的移动应用,以满足不断变化的市场需求。
18 1
|
1月前
|
机器学习/深度学习 调度 Android开发
安卓应用开发:打造高效通知管理系统
【2月更文挑战第14天】 在移动操作系统中,通知管理是影响用户体验的关键因素之一。本文将探讨如何在安卓平台上构建一个高效的通知管理系统,包括服务、频道和通知的优化策略。我们将讨论最新的安卓开发工具和技术,以及如何通过这些工具提高通知的可见性和用户互动性,同时确保不会对用户造成干扰。
33 1
|
12天前
|
XML 开发工具 Android开发
构建高效的安卓应用:使用Jetpack Compose优化UI开发
【4月更文挑战第7天】 随着Android开发不断进化,开发者面临着提高应用性能与简化UI构建流程的双重挑战。本文将探讨如何使用Jetpack Compose这一现代UI工具包来优化安卓应用的开发流程,并提升用户界面的流畅性与一致性。通过介绍Jetpack Compose的核心概念、与传统方法的区别以及实际集成步骤,我们旨在提供一种高效且可靠的解决方案,以帮助开发者构建响应迅速且用户体验优良的安卓应用。
|
14天前
|
Java Android开发
Android开发之使用OpenGL实现翻书动画
本文讲述了如何使用OpenGL实现更平滑、逼真的电子书翻页动画,以解决传统贝塞尔曲线方法存在的卡顿和阴影问题。作者分享了一个改造后的外国代码示例,提供了从前往后和从后往前的翻页效果动图。文章附带了`GlTurnActivity`的Java代码片段,展示如何加载和显示书籍图片。完整工程代码可在作者的GitHub找到:https://github.com/aqi00/note/tree/master/ExmOpenGL。
19 1
Android开发之使用OpenGL实现翻书动画