6. Window Flag 实战:FLAG_NOT_FOCUSABLE、FLAG_KEEP_SCREEN_ON、FLAG_TRANSLUCENT 等常用 Flag
Window Flag,说白了就是窗口的「行为开关」。你给窗口设置一个 Flag,系统就知道该怎么对待它——要不要抢焦点、要不要保持屏幕常亮、要不要半透明。我刚开始接触这部分时,总觉得 Flag 太多记不住,后来发现其实常用的就那么几个,吃透了它们,大部分窗口定制需求都能搞定。
6.1 FLAG_NOT_FOCUSABLE:不抢焦点的「隐形窗口」
这个 Flag 我用的最多。它的作用很简单:窗口不获取焦点,也不接收按键事件。但注意,它依然能接收触摸事件。
我在项目中遇到过这样一个场景:需要做一个全局悬浮球,点击后弹出菜单。如果悬浮球窗口抢了焦点,用户就无法操作背后的应用了。这时候 FLAG_NOT_FOCUSABLE 就派上了用场。
// 创建一个不抢焦点的悬浮窗口
WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.gravity = Gravity.TOP | Gravity.START;
// 注意:不抢焦点的窗口,默认也不会接收触摸事件
// 需要额外设置 FLAG_NOT_TOUCH_MODAL 或 FLAG_WATCH_OUTSIDE_TOUCH
params.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
6.2 FLAG_KEEP_SCREEN_ON:屏幕常亮的「守护者」
这个 Flag 我愿称之为「最省心的 Flag」。你不需要管理 WakeLock,不需要申请电源权限,只需要在 WindowManager.LayoutParams 里加一个标志位,系统就会帮你保持屏幕常亮。
为什么会这样?因为 WindowManager 在底层会帮你管理一个系统级的 WakeLock。窗口显示时自动持有,窗口移除时自动释放。你想想看,这比你自己手动管理 WakeLock 要安全多少倍?
// 视频播放器窗口保持屏幕常亮
WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
// 其他参数设置...
// 或者更简单的做法:在 Activity 的 Window 上设置
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
6.3 FLAG_TRANSLUCENT:半透明窗口的「开关」
这个 Flag 在 Android 4.4 之前是主流方案,用来实现状态栏和导航栏的透明效果。但现在我建议你优先使用 WindowInsetsController 或 SYSTEM_UI_FLAG_LAYOUT_STABLE 等新 API。
不过,FLAG_TRANSLUCENT 在自定义悬浮窗时依然有用。比如做一个半透明的引导层,或者毛玻璃效果的悬浮面板。
// 设置半透明状态栏(旧方案,兼容老设备)
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
// 设置半透明导航栏
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
android:fitsSystemWindows="true"。
6.4 其他常用 Flag 速查
除了上面三个,还有几个 Flag 我经常用到。这里整理成表格,方便你快速查阅:
| Flag 名称 | 作用 | 使用场景 |
|---|---|---|
| FLAG_NOT_TOUCH_MODAL | 窗口外的触摸事件可以穿透到下层窗口 | 悬浮窗、小工具窗口 |
| FLAG_WATCH_OUTSIDE_TOUCH | 监听窗口外的触摸事件(不消费事件) | 点击窗口外部关闭弹窗 |
| FLAG_LAYOUT_NO_LIMITS | 窗口可以绘制到屏幕安全区域之外 | 全屏沉浸式体验 |
| FLAG_FULLSCREEN | 隐藏状态栏(已废弃,建议用新 API) | 老项目兼容 |
| FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS | 允许窗口绘制系统栏背景 | 自定义状态栏颜色 |
6.5 Flag 组合实战:一个悬浮控制面板
光说不练假把式。我们来看一个实际案例:做一个悬浮控制面板,可以拖动,点击外部自动关闭,并且不干扰用户操作。
// 悬浮控制面板的 LayoutParams
WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
params.width = 300;
params.height = 400;
params.gravity = Gravity.TOP | Gravity.START;
params.x = 100;
params.y = 200;
// 组合 Flag
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
// 设置半透明背景
params.format = PixelFormat.TRANSLUCENT;
// 添加窗口
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
wm.addView(panelView, params);
6.6 Flag 优先级与冲突处理
有些 Flag 之间会互相影响。比如 FLAG_NOT_FOCUSABLE 和 FLAG_KEEP_SCREEN_ON 一起使用时,屏幕常亮依然有效——因为常亮是系统层面的行为,和焦点无关。
但有些组合就需要小心了:
- FLAG_NOT_FOCUSABLE + FLAG_NOT_TOUCHABLE:窗口既不抢焦点也不接收触摸,基本就是个「装饰品」
- FLAG_KEEP_SCREEN_ON + FLAG_DISMISS_KEYGUARD:屏幕常亮且解锁,适合导航类应用
- FLAG_TRANSLUCENT_STATUS + FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS:两者功能重叠,建议只用一个
6.7 知识体系总览
下面这张图梳理了本章的核心知识点,方便你建立整体认知:
6.8 总结
Window Flag 是窗口行为的「开关集合」。你不需要记住所有 Flag,但一定要理解每个 Flag 背后的行为逻辑。我个人习惯是:先确定窗口要不要焦点,再决定触摸事件怎么处理,最后考虑屏幕常亮等特殊需求。按这个顺序来,基本不会出错。
嗯,Flag 这块内容其实不难,但细节很多。建议你在真机上多试试不同的组合,看看实际效果。毕竟纸上得来终觉浅,绝知此事要躬行嘛。