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 等会依赖主题定义的属性

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器