如何切换主题(夜间模式)而不重新启动活动?

use*_*613 34 android android-theme

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

我没关系,直到我发现这个应用程序.它可以在两个主题之间无缝切换,也可以使用过渡/动画切换!

在此输入图像描述

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

GKA*_*GKA 40

@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>
Run Code Online (Sandbox Code Playgroud)

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

Intent intent = new Intent(this, <yourclass>.class);
startActivity(intent);
finish();
Run Code Online (Sandbox Code Playgroud)

我会做:

@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();
    }
});
Run Code Online (Sandbox Code Playgroud)

效果如视频所示......

  • 杀死创建活动和重新创建活动有什么区别..两者都会重新创建活动..只是动画不可见...如果我希望我的 editText 数据在切换主题时存在..此代码将不起作用.. (2认同)
  • 这是一个老问题,但我对这个解决方案有疑问。更准确地说,除了动画之外,一切都按预期进行。我还尝试使用自定义动画,但无论我在 xml 中设置的持续时间如何,动画都会在 20-30 毫秒后停止。您建议的库存动画也会发生同样的情况。我在用于设置的片段中使用 getActivity() 重新创建活动。 (2认同)
  • 我对此解决方案有疑问,我在活动中使用片段。当我重新启动活动时,我会丢失片段中的数据,例如滚动列表位置。 (2认同)
  • @MajidSadeghi 这是一个标准的 android 生命周期问题。保存并恢复活动生命周期挂钩中所需的信息。 (2认同)

Ale*_*sen 13

转换/动画使主题在重新启动活动时无缝变化,这可以通过在主题中添加"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>
Run Code Online (Sandbox Code Playgroud)

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

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();
Run Code Online (Sandbox Code Playgroud)

然后在您的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>);
Run Code Online (Sandbox Code Playgroud)

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

  • 虽然这在理论上可以回答这个问题,但 [最好](​​//meta.stackoverflow.com/q/8259) 在此处包含答案的基本部分,并提供链接以供参考。 (2认同)
  • 感谢反馈,我编辑了我的答案并包含了基本部分。 (2认同)

Asa*_*sad 5

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

要设置暗/亮模式,请使用以下命令:

AppCompatDelegate.setDefaultNightMode(state) //state can be AppCompatDelegate.MODE_NIGHT_YES or AppCompatDelegate.MODE_NIGHT_NO
Run Code Online (Sandbox Code Playgroud)

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

为了避免活动娱乐闪烁(为了平滑过渡),请在您的活动中添加以下方法

@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);
    }
Run Code Online (Sandbox Code Playgroud)

  • 虽然这对于多活动应用程序来说可能看起来不错,但它并不总是适合单活动/多片段应用程序。在这种情况下,像这样重新创建活动不会引导用户回到原来的位置。 (4认同)

Ima*_*kia 5

GKA 答案中super.onCreate(savedInstanceState)之前的setTheme()是完美的方法并且工作良好,感谢 GKA。

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

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

导致使用新实例重新创建此 Activity。这会导致与由于配置更改而创建 Activity 时的流程本质上相同的流程 - 当前实例将经历其生命周期到 onDestroy(),然后在其之后创建一个新实例。

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

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

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

在此输入图像描述