以编程方式更改活动主题

146
在某些情况下,我需要从我的活动中移除对话框主题,但似乎无法实现。以下是一个示例:
第一个活动:
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    startActivity(new Intent(MainActivity.this, SecondActivity.class));
}

第二个活动:

public void onCreate(Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    super.onCreate(savedInstanceState);
    setTheme(android.R.style.Theme);
    setContentView(R.layout.activity_second);
}

清单摘录:

 <activity android:name="SecondActivity" android:theme="@android:style/Theme.Dialog"></activity>

当我运行时,它仍旧是对话框主题。

API10

谢谢。

5个回答

222

根据 文档,在任何视图输出之前都必须调用setTheme。看起来super.onCreate()参与了view处理过程。

因此,要实现动态切换主题,只需在super.onCreate之前调用setTheme,如下所示:

public void onCreate(Bundle savedInstanceState) {
    setTheme(android.R.style.Theme);
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_second);
}

在我的MotoG (v1) 上,使用的是Android 5.1版本,它可以正常运行。如果您无法正常运行,请分享您的设备和Android版本。 - sdf3qrxewqrxeqwxfew3123
我必须按照这里描述的方式定义一个主题: https://dev59.com/AFwY5IYBdhLWcg3w5raq#44236460 然后按照这个答案所述设置它。它运行得很好。 - LaloLoop
有没有办法只设置一次主题,而不是每个页面都设置主题? - gayan1991
我使用共享首选项来保存主题,但是当重新启动应用程序时,会先显示第一个主题,然后才会显示第二个主题! - Mohammad Afrashteh
1
@gayan1991 你可以使用另一个活动来定义你的主题,并使所有其他活动都扩展此活动,例如: https://pastebin.com/r93qrRDG 编辑:使用Pastebin以获得更好的格式。 - SocialSupaCrew
我如何在按钮点击时切换主题? - Ali Azaz Alam

61

user1462299的回答很好,但如果包括片段,它们将使用原始活动主题。要将主题应用于所有片段,您可以覆盖上下文的getTheme()方法:

@Override
public Resources.Theme getTheme() {
    Resources.Theme theme = super.getTheme();
    if(useAlternativeTheme){
        theme.applyStyle(R.style.AlternativeTheme, true);
    }
    // you could also use a switch if you have many themes that could apply
    return theme;
}

现在您不需要在onCreate()方法中调用setTheme()。通过这种方式,您正在覆盖获取当前主题的每个请求。


2
应该在Activity还是各自的Fragment中重写getTheme()方法?我已经在Activity中实现了这个方法,但是Fragment仍然使用原始的Activity主题。 - saltandpepper
@saltandpepper 在 Activity 中覆盖它就足够了。确保你的 Fragment 代码和布局不会再次更改它。 - Björn Kechel
2
很好的观点,user1269737。因此,您应该确保没有繁重的计算。在简单条件情况下返回样式不会影响性能。 - Björn Kechel
2
在2021年,这不是一个好的解决方案。我已经发布了这段代码,它似乎频繁调用getTheme,而applyStyle是一个相对较重的操作。在相对低端的设备上,我可以通过多次进出片段来耗尽我的应用程序内存和OOM。因此,要么您需要确保只调用一次applyStyle,要么只需使用已接受的答案(https://dev59.com/1mgu5IYBdhLWcg3wHjl7#11576546),这些天确实也会在片段上设置主题,并且当然只被调用一次。 - Daniel Wilson
1
“getTheme” 在整个程序中都被调用,因此在使其幂等方面要非常小心。如果您要将主题保存到虚拟机中(我从未见过这种情况),我建议将其放在首选项或某个应用程序级别的上下文中。 - Daniel Wilson
显示剩余3条评论

15

我知道我晚了,但我想在这里发布一个解决方案:
检查完整的源代码此处
这是我在使用首选项更改主题时使用的代码。

SharedPreferences pref = PreferenceManager
        .getDefaultSharedPreferences(this);
String themeName = pref.getString("prefSyncFrequency3", "Theme1");
if (themeName.equals("Africa")) {
    setTheme(R.style.AppTheme);



} else if (themeName.equals("Colorful Beach")) {
    //Toast.makeText(this, "set theme", Toast.LENGTH_SHORT).show();
    setTheme(R.style.beach);


} else if (themeName.equals("Abstract")) {
    //Toast.makeText(this, "set theme", Toast.LENGTH_SHORT).show();

    setTheme(R.style.abstract2);

} else if (themeName.equals("Default")) {

    setTheme(R.style.defaulttheme);

}
请注意,在设置内容视图之前必须放置代码...
编码愉快!

我使用共享偏好保存主题,但是在重新启动应用程序时,第一个主题会短暂地出现,然后才会显示第二个主题! - Mohammad Afrashteh

5

我使用了这段代码来实现暗黑模式...它对我来说效果很好...你可以在一个开关上使用它....监听器...

//setting up Night Mode...
 AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
//Store current mode in a sharedprefernce to retrieve on restarting app
            editor.putBoolean("NightMode", true);
            editor.apply();
//restart all the activities to apply changed mode...
            TaskStackBuilder.create(getActivity())
                    .addNextIntent(new Intent(getActivity(), MainActivity.class))
                    .addNextIntent(getActivity().getIntent())
                    .startActivities();


问题是8年前提出的,现在您的解决方案用于应用主题,但我遇到了一个问题,主题未应用于所有活动,我为浅色和深色定义了一些样式朋友,分别用于文本,但这些并没有应用,即使我尝试按照您提到的重启活动。您的解决方案最接近我的问题。 - Azhar Ali

4

这个对我来说很好用:

theme.applyStyle(R.style.AppTheme, true)

用法:

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    //The call goes right after super.onCreate() and before setContentView()
    theme.applyStyle(R.style.AppTheme, true)
    setContentView(layoutId)
    onViewCreated(savedInstanceState)
}

或者使用 setTheme(R.style.AppTheme_Light)

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    //The call goes right after super.onCreate() and before setContentView()
    setTheme(R.style.AppTheme_Light)
    setContentView(R.layout.activity_main)
}

主题在哪里? - famfamfam
请在您的styles.xml或themes.xml中添加@famfamfam。 - Tamim Attafi
@famfamfam 我更新了我的答案并添加了另一个选项。 - Tamim Attafi
谢谢,它可以工作。 - famfamfam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接