Window概述

Post on Feb 02, 2023 by Wei Lin

概述

View、Window与Activity

Window概述1.png

(1) Activity

Activity就像一个控制器,通过其它方法来与Window和View进行交互,但并不实际控制View,实际上Activity的setContentView底层通过Window完成。


(2) Window

Window表示一个窗口的概念,是所有View的直接管理者,任何View都通过Window呈现。


  • Window是视图的承载器,内部持有一个 DecorView,而这个DecorView才是View的根布局;
  • Window是一个抽象类,实际在Activity中持有的是其子类PhoneWindow;
  • PhoneWindow中有个内部类DecorView,通过创建DecorView来加载Activity.setContentView()设置的layout布局;
  • Window 通过WindowManager将DecorView加载其中,并将DecorView交给ViewRootImpl,进行视图绘制以及其他交互。


(3) DecorView及View

View可以理解为是屏幕上一块绘制各种UI元素并可以响应用户输入的一个区域。

DecorView是FrameLayout的子类,它可以被认为是 Android 视图树的根视图。

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {}

Window与Activity区别

Window 和 Activity 是 Android 中两个不同的概念,它们之间有如下的区别。


(1) 概念

  • Window(窗口):Window 是 Android 中的一个抽象概念,表示一个界面窗口。它可以包含一个或多个 View,并用于显示用户界面。
  • Activity(活动):Activity 是 Android 应用程序的基本组件,代表用户与应用程序进行交互的一个屏幕。每个 Activity 都有自己的生命周期和界面布局。


(2) 角色

  • Window:Window 是 Activity 的一个重要组成部分,用于承载 Activity 的内容视图和处理窗口相关的属性和行为。
  • Activity:Activity 是应用程序的一个独立单元,负责管理界面和用户交互,它通过 setContentView() 方法将内容视图设置到 Window 中进行显示。


(3) 功能

  • Window:Window 可以控制窗口的样式、特性、大小、位置、背景、动画效果等。它提供了一些方法和属性,用于管理窗口的外观和行为。
  • Activity:Activity 负责管理界面的生命周期,处理用户交互和逻辑。它提供了一些生命周期方法和回调,用于管理 Activity 的状态和行为。


(4) 关系

  • 一个 Activity 对应一个 Window 对象。Activity 是 Window 的控制者,通过 Window 对象来管理窗口的内容和属性。
  • 一个应用程序可以有多个 Activity,每个 Activity 都有自己的窗口,但它们共享同一个应用程序的上下文和资源。


总结来说,Window 是用于承载界面内容和管理窗口属性的抽象概念,而 Activity 则是应用程序的基本组件,负责界面管理和用户交互。Activity通过设置Window的内容视图和属性来展示界面,并通过生命周期方法处理界面的状态和行为。

Window和View

在Android中Window是一个抽象的概念,Android所有的视图都是通过Window来呈现,不论是Activity、Dialog还是Toast,视图实际都可以看成是附加在window上,即Window是View的载体。


Android应用中的EditView,ImageView等都是View,Window是不可见的,能看到的是View,而一棵View树可以看作一个Window。Window本身并不实体存在,是一个抽象,比如班集体这个抽象的概念,而看得见的学生和教室可以看作View。


而设计Window的理念就是处理View的显示层级,比如在Activity上弹出一个Dialog,又弹出一个Toast,那么该如何保证Dialog显示是在Activity上的,而Toast又是在Dialog上的;这时刷新Activity的UI,Dialog的UI是否需要刷新,而把这些View树给分开,使用Window管理,就可以方便实现不同View树的分层级显示;另一个重要作用是方便点击事件的分发,还是前面的例子,这时给屏幕一个点击事件,这时是Dialog响应点击事件还是Activity响应点击事件,这个也可以由Window来实现。


总的来说,设计出Window就是为了解耦,虽然显示还是View来显示,把View树给看成一个集体,这样在处理显示和事件传递就非常方便了。

Window相关类

Window

代码位置:frameworks/base/core/java/android/view/Window.java

作用:Window类是一个抽象类,表示一个窗口的概念,是所有View的直接管理者,任何View都通过Window呈现。Android中PhoneWindow继承了Window类


(1) Window类的相关属性

// 所属的Activity
private final Context mContext;

@UnsupportedAppUsage
private TypedArray mWindowStyle;
// 设置Window回调,是一个Window.Callback对象,由Activity设置
@UnsupportedAppUsage
private Callback mCallback;
// 设置其它相关回调
private OnWindowDismissedCallback mOnWindowDismissedCallback;
private OnWindowSwipeDismissedCallback mOnWindowSwipeDismissedCallback;
private WindowControllerCallback mWindowControllerCallback;
@WindowInsetsController.Appearance
private int mSystemBarAppearance;
private DecorCallback mDecorCallback;
private OnRestrictedCaptionAreaChangedListener mOnRestrictedCaptionAreaChangedListener;
private Rect mRestrictedCaptionAreaRect;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private WindowManager mWindowManager;
@UnsupportedAppUsage
private IBinder mAppToken;
@UnsupportedAppUsage
private String mAppName;
@UnsupportedAppUsage
private boolean mHardwareAccelerated;
private Window mContainer;
private Window mActiveChild;
private boolean mIsActive = false;
private boolean mHasChildren = false;
private boolean mCloseOnTouchOutside = false;
private boolean mSetCloseOnTouchOutside = false;
private int mForcedWindowFlags = 0;

@UnsupportedAppUsage
private int mFeatures;
@UnsupportedAppUsage
private int mLocalFeatures;

private boolean mHaveWindowFormat = false;
private boolean mHaveDimAmount = false;
private int mDefaultWindowFormat = PixelFormat.OPAQUE;

private boolean mHasSoftInputMode = false;

@UnsupportedAppUsage
private boolean mDestroyed;

private boolean mOverlayWithDecorCaptionEnabled = true;
private boolean mCloseOnSwipeEnabled = false;

// The current window attributes.
@UnsupportedAppUsage
private final WindowManager.LayoutParams mWindowAttributes =
    new WindowManager.LayoutParams();


(2) Window的常用方法

常用方法 说明
setContentView(View view) 设置窗口的内容视图,即显示在窗口中的主要 UI 控件
addContentView(View view, ViewGroup.LayoutParams params) 添加附加的内容视图到窗口中
setBackgroundDrawable(Drawable drawable) 设置窗口的背景图像
setAttributes(WindowManager.LayoutParams params) 设置窗口的属性,如位置、大小、透明度等
setLayout(int width, int height) 设置窗口的布局大小
setFlags(int flags, int mask) 设置窗口的标志位,如全屏、透明状态栏等
getDecorView() 获取窗口的根视图
getAttributes() 获取窗口的属性信息

这些方法用于控制窗口的外观和行为,如设置窗口的内容、背景、大小、位置等。请注意,具体的 Window 实现类可能会添加额外的成员属性来支持更多的窗口管理功能,但是这些成员属性通常不是公开的,而是由 Window 类的方法来间接操作和管理。开发者在使用 Window 类时,主要是通过这些方法来控制窗口的行为和样式。

WindowManager

public interface WindowManager extends ViewManager {...}

WindowManager是一个APP可调用的接口,继承于接口类ViewManager,具体实现类是WindowMangerImpl(没有实现什么功能),使用桥接模式将功能委托给WindowManagerGlobal。

WindowManager用来管理Window,实现Window(View)的添加、更新和删除操作。

常用方法 说明
addView(View view, WindowManager.LayoutParams params) 向窗口中添加一个 View,并指定窗口的布局参数(WindowManager.LayoutParams)。通过这个方法,可以在屏幕上创建一个悬浮窗口或自定义视图。
removeView(View view) 从窗口中移除一个 View,即关闭或移除已添加的窗口
getDefaultDisplay() 获取默认的 Display 对象,用于获取屏幕尺寸、密度等信息
removeViewImmediate(View view) 立即移除一个 View,与 removeView(View view) 不同的是,该方法会立即执行,不会等待动画完成
getDefaultDisplay() 获取默认的 Display 对象,用于获取屏幕的一些属性,例如屏幕尺寸、密度等
getDefaultDisplaySize(Point outSize) 获取默认的屏幕尺寸,返回的是屏幕的实际宽高,不受屏幕旋转影响

WindowManagerService

WindowManager提供的很多功能最终通过WindowManagerGlobal.getWindowManagerService()由WMS处理。WindowManagerService是负责管理窗口的核心服务。作为系统级服务,它主要用于处理应用程序和系统窗口的创建、显示、移动、更新、移除等窗口管理任务。

由于 WindowManagerService 是系统级服务,其方法不会直接暴露给应用开发者使用。下面是 WindowManagerService 的一些常用方法和功能,这些方法是在系统级别进行窗口管理时使用的。

常用方法 说明
addWindow() 添加一个窗口到 WindowManagerService 中,用于显示应用程序的窗口
removeWindow() 从 WindowManagerService 中移除一个窗口,用于关闭或移除应用程序的窗口
attachToDisplayContent(IBinder clientToken, int displayId) 将一个窗口(Window)与指定的显示(Display)关联起来
displayReady() 通知系统指定的显示设备已经准备好显示内容

ViewRootImpl

ViewRootImpl 是 View 的最高层级,是所有 View 的根。ViewRootImpl是WindowManager和DecorView之间的桥梁。

View的三大流程 (measure、layout、draw) 和事件分发等都是通过ViewRootImpl来执行的。addView()、removeView()、update()等方法的调用顺序:WindowManagerImpl -> WindowManagerGlobal -> ViewRootImpl

相关属性 说明
IWindowSession mWindowSession 与 WindowManagerService 连接的会话
Display mDisplay 保存该窗口显示的屏幕
String mBasePackageName 该窗口所归属的应用包名
WindowManager.LayoutParams mWindowAttributes 窗口属性参数
View mView 该窗口要显示的 View 树
boolean mFirst 记录该窗口是否第一次刷新
boolean mAdded 记录该窗口是否添加成功

Display

在 Android 中,Display对象代表了设备的屏幕显示。它提供了访问屏幕尺寸、分辨率、密度以及其他显示相关信息的方法。通过Display对象,可以获取和管理设备的显示属性。

以下是一些常用的Display相关方法和功能:

功能 说明
getDefaultDisplay() 获取默认的显示对象 Display display = context.getDefaultDisplay();
getSize() getWidth() getHeight() 获取显示的尺寸和分辨率 可以使用Display对象的getSize()方法或getWidth()和getHeight()方法来获取屏幕的宽度和高度。另外,还可以使用getRealSize()方法获取屏幕的实际物理尺寸,而不考虑旋转或系统装饰的影响
getDensity() 获取屏幕的逻辑密度(density),即每英寸的像素点数
getDpi() 获取屏幕的物理密度(dpi)
getRotation() 获取显示的旋转角度 返回值为Surface类中定义的常量,如Surface.ROTATION_0、Surface.ROTATION_90等
isValid() 判断显示是否可用,即显示对象是否有效

其它

Window的flags

// 当 Window 可见时允许锁屏
public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001;
 
// Window 后面的内容都变暗
public static final int FLAG_DIM_BEHIND = 0x00000002;
 
@Deprecated
// API 已经过时,Window 后面的内容都变模糊
public static final int FLAG_BLUR_BEHIND = 0x00000004;
 
// Window 不能获得输入焦点,即不接受任何按键或按钮事件,例如该 Window 上 有 EditView,点击 EditView 是 不会弹出软键盘的
// Window 范围外的事件依旧为原窗口处理;例如点击该窗口外的view,依然会有响应。另外只要设置了此Flag,都将会启用FLAG_NOT_TOUCH_MODAL
public static final int FLAG_NOT_FOCUSABLE = 0x00000008;
 
// 设置了该 Flag,将 Window 之外的按键事件发送给后面的 Window 处理, 而自己只会处理 Window 区域内的触摸事件
// Window 之外的 view 也是可以响应 touch 事件。
public static final int FLAG_NOT_TOUCH_MODAL = 0x00000020;
 
// 设置了该Flag,表示该 Window 将不会接受任何 touch 事件,例如点击该 Window 不会有响应,只会传给下面有聚焦的窗口。
public static final int FLAG_NOT_TOUCHABLE = 0x00000010;
 
// 只要 Window 可见时屏幕就会一直亮着
public static final int FLAG_KEEP_SCREEN_ON = 0x00000080;
 
// 允许 Window 占满整个屏幕
public static final int FLAG_LAYOUT_IN_SCREEN = 0x00000100;
 
// 允许 Window 超过屏幕之外
public static final int FLAG_LAYOUT_NO_LIMITS   = 0x00000200;
 
// 全屏显示,隐藏所有的 Window 装饰,比如在游戏、播放器中的全屏显示
public static final int FLAG_FULLSCREEN = 0x00000400;
 
// 表示比FLAG_FULLSCREEN低一级,会显示状态栏
public static final int FLAG_FORCE_NOT_FULLSCREEN = 0x00000800;
 
// 当用户的脸贴近屏幕时(比如打电话),不会去响应此事件
public static final int FLAG_IGNORE_CHEEK_PRESSES = 0x00008000;
 
// 则当按键动作发生在 Window 之外时,将接收到一个MotionEvent.ACTION_OUTSIDE事件。
public static final int FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000;
 
@Deprecated
// 窗口可以在锁屏的 Window 之上显示, 使用 Activity#setShowWhenLocked(boolean) 方法代替
public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;
 
// 表示负责绘制系统栏背景。如果设置,系统栏将以透明背景绘制,
// 此 Window 中的相应区域将填充 Window#getStatusBarColor()和 Window#getNavigationBarColor()中指定的颜色。
public static final int FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000;
 
// 表示要求系统壁纸显示在该 Window 后面,Window 表面必须是半透明的,才能真正看到它背后的壁纸
public static final int FLAG_SHOW_WALLPAPER = 0x00100000;

SoftInputMode(软键盘)相关flags

// 没有指定状态,系统会选择一个合适的状态或者依赖于主题的配置
public static final int SOFT_INPUT_STATE_UNCHANGED = 1;
 
// 当用户进入该窗口时,隐藏软键盘
public static final int SOFT_INPUT_STATE_HIDDEN = 2;
 
// 当窗口获取焦点时,隐藏软键盘
public static final int SOFT_INPUT_STATE_ALWAYS_HIDDEN = 3;
 
// 当用户进入窗口时,显示软键盘
public static final int SOFT_INPUT_STATE_VISIBLE = 4;
 
// 当窗口获取焦点时,显示软键盘
public static final int SOFT_INPUT_STATE_ALWAYS_VISIBLE = 5;
 
// window会调整大小以适应软键盘窗口
public static final int SOFT_INPUT_MASK_ADJUST = 0xf0;
 
// 没有指定状态,系统会选择一个合适的状态或依赖于主题的设置
public static final int SOFT_INPUT_ADJUST_UNSPECIFIED = 0x00;
 
// 当软键盘弹出时,窗口会调整大小,例如点击一个EditView,整个layout都将平移可见且处于软件盘的上方
// 同样的该模式不能与SOFT_INPUT_ADJUST_PAN结合使用;
// 如果窗口的布局参数标志包含FLAG_FULLSCREEN,则将忽略这个值,窗口不会调整大小,但会保持全屏。
public static final int SOFT_INPUT_ADJUST_RESIZE = 0x10;
 
// 当软键盘弹出时,窗口不需要调整大小, 要确保输入焦点是可见的,
// 例如有两个EditView的输入框,一个为Ev1,一个为Ev2,当你点击Ev1想要输入数据时,当前的Ev1的输入框会移到软键盘上方
// 该模式不能与SOFT_INPUT_ADJUST_RESIZE结合使用
public static final int SOFT_INPUT_ADJUST_PAN = 0x20;
 
// 将不会调整大小,直接覆盖在window上
public static final int SOFT_INPUT_ADJUST_NOTHING = 0x30;