概述
View、Window与Activity
(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;