📖 故事背景:画框工厂(PhoneWindow)的订单处理
想象一家为「世界名画(Activity)」生产画框的工厂:
- 客户:
Activity
(需要展示的画作) - 工厂:
PhoneWindow
(Android窗口系统核心) - 画框:
DecorView
(最终展示的带装饰的根视图)
🔧 第一步:创建空白画框(DecorView诞生)
工厂接到订单后,先制作基础画框:
java
// 源码定位:PhoneWindow.installDecor()
public void installDecor() {
if (mDecor == null) {
// 👇 用系统模板创建原始画框
mDecor = generateDecor();
}
}
protected DecorView generateDecor() {
return new DecorView(getContext(), -1); // 创建纯净的DecorView
}
💡 关键点:此时
DecorView
是空白的FrameLayout,仅带系统主题背景(如窗口背景色)。
🖼️ 第二步:安装标准画框模板(系统预置布局)
工厂根据客户需求选择画框模板:
java
// 源码定位:PhoneWindow.generateLayout()
protected ViewGroup generateLayout(DecorView decor) {
// 1️⃣ 根据主题选择模板(如全屏/带标题栏)
int layoutResource = R.layout.screen_title; // 预置布局ID
// 2️⃣ 将模板inflate到DecorView中
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
// 3️⃣ 添加关键系统视图容器
ViewGroup contentParent = findViewById(ID_ANDROID_CONTENT); // 👉 就是R.id.content!
return contentParent;
}
🌟 模板示例:
screen_title.xml
包含标题栏(TitleBar)和预留内容区域(R.id.content)
🎨 第三步:装入客户画作(开发者布局挂载)
当开发者调用setContentView()
时:
java
// 源码定位:PhoneWindow.setContentView()
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor(); // 确保画框已创建
}
// 👇 将开发者的布局装入预留区域
mLayoutInflater.inflate(layoutResID, mContentParent);
}
✅ 最终视图层级:
text
DecorView (FrameLayout) ├── LinearLayout (系统模板) │ ├── TitleBar │ └── FrameLayout (id=android.R.id.content) 👈 你的布局在这里!
🚦 第四步:协调系统装饰(状态栏/导航栏适配)
画框工厂需处理系统装饰条(状态栏/导航栏)的覆盖问题:
java
// 源码定位:DecorView.fitSystemWindows()
protected boolean fitSystemWindows(Rect insets) {
// 1️⃣ 计算系统栏占据的区域
Rect systemInsets = new Rect(insets);
// 2️⃣ 调整预留内容区域(避免被遮挡)
View contentView = findViewById(android.R.id.content);
contentView.setPadding(
systemInsets.left,
systemInsets.top, // 状态栏高度
systemInsets.right,
systemInsets.bottom // 导航栏高度
);
return true;
}
⚡ 现代方案:Android 10+ 使用
WindowInsetsController
动态控制边衬区
🎭 高级玩法:沉浸式体验(画框魔术)
当调用全屏模式时,工厂施展「隐藏画框」魔术:
java
// 源码定位:DecorView.setSystemUiVisibility()
public void setSystemUiVisibility(int vis) {
// 1️⃣ 隐藏状态栏(画框顶部装饰条消失)
if ((vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0) {
mStatusBarView.setVisibility(View.GONE);
}
// 2️⃣ 隐藏导航栏(画框底部装饰条消失)
if ((vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0) {
mNavigationBarView.setVisibility(View.GONE);
}
}
🔥 注意:用户触摸屏幕时,系统装饰条会重新显示(画框自动复原)
💎 核心原理总结(小白速记版)
组件 | 比喻 | 源码关键点 |
---|---|---|
PhoneWindow | 画框工厂 | 管理DecorView 的创建与配置 |
DecorView | 成品画框 | 继承FrameLayout 的根视图 |
R.id.content | 画作悬挂区 | 开发者布局的父容器 |
fitSystemWindows | 装饰条避让算法 | 调整内边距避免遮挡(旧方案) |
WindowInsets | 动态装饰条测量器 | 现代边衬区处理方案(Android 10+) |
🛠️ 开发者的两个必知技巧
- 获取DecorView的代码模板
java
// 在Activity中获取DecorView
View decorView = getWindow().getDecorView();
// 获取内容区域(你的布局父容器)
ViewGroup content = findViewById(android.R.id.content);
- 动态修改系统栏颜色
java
// Android 5.0+ 修改状态栏颜色
getWindow().setStatusBarColor(Color.RED);
// Android 8.0+ 修改导航栏颜色
getWindow().setNavigationBarColor(Color.BLUE);
⚡ 真相时刻:为什么叫DecorView?
Decor = Decoration(装饰)
它本质上是一个为Activity内容添加系统级装饰的顶级容器:
- 装饰包括:标题栏/状态栏/导航栏/窗口背景
- 你的布局只是装饰框中的核心画作!
就像卢浮宫的《蒙娜丽莎》需要华丽画框衬托,你的Activity也需要DecorView这个「智能画框」来适配千变万化的Android设备!🖼️✨