setTheme
override fun onCreate(savedInstanceState: Bundle?) {
// 设置为正式主题(恢复)
setTheme(R.style.AppTheme)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
setTheme(R.style.AppTheme)会影响后续那些流程
这行代码:
setTheme(R.style.AppTheme)
在 onCreate() 的 super.onCreate() 之前调用,是为了覆盖 Manifest 中配置的临时启动主题(如 Theme.App.Launch),使得后续创建的 View、Window、资源加载等使用正式主题。
setTheme() 的作用分析
setTheme() 本质上是设置当前 Activity 的 Context 对应的 Theme ID,它会影响后续所有使用该 Context 创建的资源。
🔍 对后续流程的影响主要有:
影响流程 | 说明 |
---|---|
LayoutInflater.inflate() | 在 setContentView() 中加载布局时,所有 View 的样式(如颜色、背景、字体、shape)都从 Context.getTheme() 获取 —— 会使用你通过 setTheme() 设置的主题。 |
getTheme() / obtainStyledAttributes() | 若你在代码中读取主题属性(如 context.obtainStyledAttributes(…)),都会基于该主题来返回结果。 |
Window 样式 | 部分主题属性(如是否全屏、状态栏颜色、标题栏)也会影响 Window。 |
MaterialComponent 主题绑定组件(如 Button、TextView、AppBar) | 它们的默认样式也是从当前主题读取的,必须切到正式主题才能生效 |
调用时机为何要在 super.onCreate() 之前?
super.onCreate() 内部会做很多事情,包括:
• 初始化 Window;
• 创建 ViewRootImpl;
• 绑定 LayoutInflater;
• 调用 setContentView() 的实际实现。
如果你晚于 super.onCreate() 再调用 setTheme(),那么:
• 布局已经加载;
• View 已经读取了旧主题样式;
• 即便你切了主题,也不会重新生效 —— 你看到的 UI 会异常或失效。
一个典型影响的例子:
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.AppTheme) // 设置正式主题
super.onCreate(savedInstanceState)
val btn = Button(this) // 这个 Button 的样式来自 R.style.AppTheme 中配置的 style
}
如果你不调用 setTheme(),而 Manifest 中还是 Theme.App.Launch,那这个 Button 的背景、字体、shape 都可能是默认的,看起来不对。
核心机制在哪?
setTheme() 设置的值会被保存在 ContextThemeWrapper 中的 mThemeResource 和 mTheme 中。
// ContextThemeWrapper.java
public void setTheme(int resid) {
if (mThemeResource != resid) {
mThemeResource = resid;
mTheme = null;
}
}
然后所有使用 Context.getTheme() 的地方就会使用你设置的主题。
🔚 总结
setTheme(R.style.AppTheme) 影响:
模块 | 影响内容 |
---|---|
View 渲染 | 控件的颜色、字体、大小、Shape 等样式 |
布局加载 | LayoutInflater 根据 Theme 渲染 View |
状态栏、标题栏 | Theme 设置可能影响系统栏样式 |
自定义组件样式 | obtainStyledAttributes() 获取样式的结果 |
Material Design 组件 | MaterialButton、AppBarLayout 等会依赖主题定义的属性 |