【问题标题】:Implementing user choice of theme实现用户选择主题
【发布时间】:2012-02-07 08:32:17
【问题描述】:

我想让用户在几个不同的主题之间进行选择,并且想知道这是否是一种合适的做事方式。我用这个方法做了一点测试,效果很好,但是我觉得可能有更好的方法,以后可能会出现一些问题,所以想问一下。

我正在考虑为每个主题创建不同的布局,并在onCreate 中为setContentView() 方法设置一个开关。我会先加载一个保存的SharedPreference 值(整数),然后根据该值显示相应的布局。显然,用户可以通过按钮或其他方式更改SharedPreference 的值。

由于这些布局基本相同,但颜色不同,我想为我的TextViews 和每个布局文件中的其他视图使用相同的 ID。我的主要问题是这会导致问题吗?

抱歉,没有代码的文字墙。我只是想大致了解这种情况下的良好做法。提前致谢。

【问题讨论】:

    标签: android android-theme


    【解决方案1】:

    我实际上在我的应用程序中具有此功能,此外,我允许用户在运行时更改主题。由于从首选项中读取值需要一些时间,因此我通过保存缓存值的全局可访问函数获取主题 ID。

    正如已经指出的 - 创建一些 Android 主题,使用 this guide。您的styles.xml 文件中至少有两个<style> 项目。例如:

    <style name="Theme.App.Light" parent="@style/Theme.Light">...</style>
    <style name="Theme.App.Dark" parent="@style/Theme">...</style>
    

    现在,您必须将其中一种样式应用于您的活动。我在活动的onCreate 方法中这样做,在任何其他调用之前:

    setTheme(MyApplication.getThemeId());
    

    getThemeId是一个返回缓存主题ID的方法:

    public static int getThemeId()
    {
        return themeId;
    }
    

    该字段正在被其他方法更新:

    public static void reloadTheme()
    {
        themeSetting = PreferenceManager.getDefaultSharedPreferences(context).getString("defaultTheme", "0");
        if(themeSetting.equals("0"))
            themeId = R.style.Theme_Light;
        else
            themeId = R.style.Theme_Dark;
    }
    

    当首选项发生变化时(当然在启动时)会调用它。这两个方法位于MyApplication 类中,该类扩展了Application。偏好更改侦听器在本文末尾进行了描述,位于主活动类中。

    最后一件非常重要的事情——在活动开始时应用主题。假设,您只能在首选项屏幕中更改主题,并且只有一种方法可以到达那里,即只有一个(主要)活动,当您退出首选项屏幕时,此活动不会重新启动 - 旧主题仍然是用过的。这是解决方法(重新启动您的主要活动):

    @Override
    protected void onResume() {
        super.onResume();
        if(schduledRestart)
        {
            schduledRestart = false;
            Intent i = getBaseContext().getPackageManager().getLaunchIntentForPackage( getBaseContext().getPackageName() );
            i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(i);
        }
    }
    

    scheduledRestart 是一个布尔变量,最初设置为 false。当这个监听器改变主题时,它设置为 true,这也会更新前面提到的缓存主题 ID:

    private class ThemeListener implements OnSharedPreferenceChangeListener{
    
        @Override
        public void onSharedPreferenceChanged(SharedPreferences spref, String key) {
            if(key.equals("defaultTheme") && !spref.getString(key, "0").equals(MyApplication.getThemeSetting()))
            {
                MyApplication.reloadTheme();
                schduledRestart = true;
            }
        }
    
    
    sp = PreferenceManager.getDefaultSharedPreferences(this);
    
    listener = new ThemeListener();
    sp.registerOnSharedPreferenceChangeListener(listener);
    

    记住要持有对侦听器对象的引用,否则它将被垃圾回收(并且将停止工作)。

    【讨论】:

    • 我认为我可以在不扩展应用程序的情况下解决问题,但问题是如果我执行 getApplicationInfo().theme 不会更新getApplication().setTheme(myThemeId)...所以是的,你的方式是正确的。
    • 现在似乎有一种更简单的方法可以重新开始活动:recreate (developer.android.com/reference/android/app/…)。
    • 真的需要重新启动吗?有些应用没有?
    【解决方案2】:

    如果您使用Material Components themes 并遵循明暗主题指南,那么您可以从AppCompatDelegate 进行操作。这些主题可以在运行时更改/应用,而无需重新启动您的应用程序。

    private fun handleThemeChange(theme: String) {
            when (newTheme) {
                getString(R.string.light) -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
                getString(R.string.dark) -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
                getString(R.string.system) -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
    
            }
        }
    

    【讨论】:

      【解决方案3】:

      您还可以使用以下方法动态更改主题:

      ContextThemeWrapper w = new ContextThemeWrapper(this, <newTHEMEId>);
      getTheme().setTo(w.getTheme());
      

      在每个活动的 onCreate 之前。

      【讨论】:

      • 我可以在 Application 类中应用它而不是在每个 Activity 中应用它吗?
      【解决方案4】:

      如果你这样做,它确实有效,我认为这不会造成任何问题,但它似乎很麻烦(你必须将所有布局乘以你想要添加的所有主题。如果以后你想修改布局中的资源,你必须在所有主题中修改它。你肯定会忘记一个)

      为什么不使用 Android 的Styles and Themes 功能?

      它们可以很容易地应用于整个活动:

      <activity android:theme="@style/my_theme">
      

      因此,当您检测到您使用的 SharedPreferences 值发生变化时(偏好活动上的按钮等),您只需切换样式即可。或者更好的是,您可以设置样式以在运行时(创建活动时)读取您的偏好值并相应地应用正确的样式/主题。

      【讨论】:

      • 我将阅读样式和主题功能并尝试这样做,谢谢您的信息。会在一小时内给你投票,因为我昨天把它们都用完了:P
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-01-12
      • 2012-07-27
      • 1970-01-01
      • 2014-10-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多