如何在不重新启动活动的情况下切换主题(夜间模式)?

Posted

技术标签:

【中文标题】如何在不重新启动活动的情况下切换主题(夜间模式)?【英文标题】:How to switch themes (night mode) without restarting the activity? 【发布时间】:2015-07-22 08:00:22 【问题描述】:

我做了一些支持多个主题的应用程序,但是当用户切换主题时我总是不得不重新启动应用程序,因为setTheme()需要在setContentView()之前调用。

在我发现这个应用程序之前,我还可以接受。它可以在两个主题之间无缝切换,还可以使用过渡/动画!

请给我一些关于这是如何实现的提示(以及动画)。谢谢!

【问题讨论】:

视频是真正的应用吗? @Blackbelt 是的,我录制了视频/gif。该应用名为“知乎”。 嘿朋友,你有没有找到像知乎这样的改变主题的解决方案? @JohnError 还没有。你有解决方案吗?如果您正在寻找一个,请对问题进行投票以提高知名度。 @user1032613 有没有找到解决办法,现在很多应用都在做? 【参考方案1】:

@Alexander Hanssen 的回答基本上已经回答了这个... 不知道为什么不被接受...可能是因为finish()/startActivity()。 我投了票,我试图发表评论,但不能......

不管怎样,我会按照他描述的风格去做。

<style name="AppThemeLight" parent="Theme.AppCompat.Light">
    <!-- Customize your theme here. -->
    <item name="android:windowAnimationStyle">@style/WindowAnimationTransition</item>
</style>
<style name="AppThemeDark" parent="Theme.AppCompat">
    <!-- Customize your theme here. -->
    <item name="android:windowAnimationStyle">@style/WindowAnimationTransition</item>
</style>
<!-- This will set the fade in animation on all your activities by default -->
<style name="WindowAnimationTransition">
    <item name="android:windowEnterAnimation">@android:anim/fade_in</item>
    <item name="android:windowExitAnimation">@android:anim/fade_out</item>
</style>

但不是以新的意图完成/开始:

Intent intent = new Intent(this, <yourclass>.class);
startActivity(intent);
finish();

我愿意:

@Override
protected void onCreate(Bundle savedInstanceState) 

    // MUST do this before super call or setContentView(...)
    // pick which theme DAY or NIGHT from settings
    setTheme(someSettings.get(PREFFERED_THEME) ? R.style.AppThemeLight : R.style.AppThemeDark);

    super.onCreate(savedInstanceState);


// Somewhere in your activity where the button switches the theme
btn.setOnClickListener(new View.OnClickListener() 
    @Override
    public void onClick(View v) 

        // decide which theme to use DAY or NIGHT and save it
        someSettings.save(PREFFERED_THEME, isDay());

        Activity.this.recreate();
    
);

效果如视频所示...

【讨论】:

杀死创建活动和重新创建活动有什么区别......两者都将重新创建活动......只是那个动画将不可见......如果我希望我的 editText 数据在切换主题时出现。 .此代码不起作用.. 这是一个老问题,但我对这个解决方案有疑问。更准确地说,除了动画之外,一切都按预期工作。我也尝试使用自定义动画,但无论我在 xml 中设置的持续时间如何,动画都会在 20-30 毫秒后停止。您建议的股票动画也会发生同样的情况。我在用于设置的片段中使用 getActivity() 重新创建活动。 @GKA 其他活动呢?更改反映在同一个活动中,但我是否需要在每个活动中应用相同的主题? @KishanSolanki 我在我的主要活动中这样做。我认为主题适用于整个应用程序。 @MajidSadeghi 这是一个标准的 android 生命周期问题。在活动的生命周期挂钩中保存和恢复您需要的信息。【参考方案2】:

当您重新启动 Activity 时,过渡/动画会无缝地更改主题,这可以通过将项目“android:windowanimationStyle”添加到您的主题中来完成,然后引用您指定 Activity 应该如何动画的样式它进入和退出。 请注意,这会使动画应用于具有该主题的所有活动。

<style name="AppThemeLight" parent="Theme.AppCompat.Light">
    <!-- Customize your theme here. -->
    <item name="android:windowAnimationStyle">@style/WindowAnimationTransition</item>
</style>
<style name="AppThemeDark" parent="Theme.AppCompat">
    <!-- Customize your theme here. -->
    <item name="android:windowAnimationStyle">@style/WindowAnimationTransition</item>
</style>
<!-- This will set the fade in animation on all your activities by default -->
<style name="WindowAnimationTransition">
    <item name="android:windowEnterAnimation">@android:anim/fade_in</item>
    <item name="android:windowExitAnimation">@android:anim/fade_out</item>
</style>

然后,当您想更改主题时,您可以在单击按钮时执行此操作:

AppSettings settings = AppSettings.getInstance(this);
settings.set(AppSettings.Key.USE_DARK_THEME,
!settings.getBoolean(AppSettings.Key.USE_DARK_THEME));
Intent intent = new Intent(this, <yourclass>.class);
startActivity(intent);
finish();

然后在您的 onCreate 方法中,使用 setTheme() 应用当前在 AppSettings 中设置的主题,如下所示:

AppSettings settings = AppSettings.getInstance(this);
setTheme(settings.getBoolean(AppSettings.Key.USE_DARK_THEME) ? R.style.AppThemeDark : R.style.AppThemeLight);
super.onCreate(savedInstanceState);
setContentView(<yourlayouthere>);

查看此要点以供参考:https://gist.github.com/alphamu/f2469c28e17b24114fe5

【讨论】:

虽然这在理论上可以回答问题,it would be preferable 在此处包含答案的基本部分,并提供链接以供参考。 感谢您的反馈,我编辑了我的答案并包含了重要部分。【参考方案3】:

对于那些试图为 android 版本 10 或更新寻找解决方案的人。

设置暗/亮模式使用这个:

AppCompatDelegate.setDefaultNightMode(state) //state can be AppCompatDelegate.MODE_NIGHT_YES or AppCompatDelegate.MODE_NIGHT_NO

它会改变你的应用程序的显示,但会闪烁

为避免 Activity 重新创建闪烁(为了平滑过渡),在您的 Activity 中添加以下方法

@Override
    public void recreate() 
        finish();
        overridePendingTransition(R.anim.anime_fade_in,
                                  R.anim.anime_fade_out);
        startActivity(getIntent());
        overridePendingTransition(R.anim.anime_fade_in,
                                  R.anim.anime_fade_out);
    

【讨论】:

虽然这对于多活动应用程序可能看起来不错,但它并不总是适用于单活动/多片段应用程序。在这种情况下,像这样重新创建 Activity 不会将用户带回到原来的位置。【参考方案4】:

没有任何东西阻止您拨打setTheme(),然后再次拨打setContentView()。您只需要稍微重新构建您的应用程序,以便在更改主题时,您需要重新初始化您可能拥有的任何持有对View 对象的引用的成员变量。

【讨论】:

你认为视频中的应用开发者使用的是什么?从动画来看,我觉得他们可能没有朝这个方向发展。 这当然是最简单的方法。我已经构建了这样的应用程序。只要您的布局不太复杂,这没什么大不了的。我能想到的唯一其他选择是编写遍历整个View 树并在每个View 上设置Theme 的代码。【参考方案5】:

setTheme() 在 GKA 答案中的 super.onCreate(savedInstanceState) 之前是完美的方法并且效果很好,这要归功于 GKA。

但它会再次为所有资源创建新实例,包括活动、片段和回收器视图。我认为这可能是繁重的工作,并导致丢失一些已保存的数据,如局部变量。

根据谷歌文档:https://developer.android.com/reference/android/app/Activity#recreate()

使用新实例重新创建此 Activity。这个结果 与创建 Activity 时的流程基本相同 配置更改——当前实例将通过其 onDestroy() 的生命周期,然后在它之后创建一个新实例。

还有另一种方法,您可以使用代码(Java 或 Kotlin)以编程方式更改主题,在这种方法中您不需要重新创建所有资源,还可以使用自定义动画,如波纹。

检查我的 GitHub 库: https://github.com/imandolatkia/Android-Animated-Theme-Manager

在这个库中,您可以创建自定义主题并使用波纹动画动态更改它们,而无需重新创建任何资源。

【讨论】:

【参考方案6】:

片段中的简单高效的一行:

requireActivity().recreate();

活动:

recreate();

【讨论】:

以上是关于如何在不重新启动活动的情况下切换主题(夜间模式)?的主要内容,如果未能解决你的问题,请参考以下文章

在不重新加载页面的情况下切换 webview 活动

在Vue 3中单击路由器链接时如何在不重新加载页面的情况下切换侧边栏

Android夜间模式切换的方法

Android夜间模式切换的方法

如何在不调用该 Activity 的 API 调用的情况下为横向和纵向模式使用不同的设计?

如何在不使用按钮的情况下切换引导模式?