【问题标题】:How to change current Theme at runtime in Android [duplicate]如何在 Android 运行时更改当前主题 [重复]
【发布时间】:2011-01-29 18:48:53
【问题描述】:

我创建了一个 PreferenceActivity,允许用户选择他想要应用到整个应用程序的主题。

当用户选择主题时,会执行以下代码:

if (...) {
    getApplication().setTheme(R.style.BlackTheme);
} else {
    getApplication().setTheme(R.style.LightTheme);
}

但是,即使我用调试器检查了代码正在执行,我也看不到用户界面有任何变化。

主题在res/values/styles.xml 中定义,Eclipse 不会显示任何错误。

<resources>
    <style name="LightTheme" parent="@android:style/Theme.Light">
    </style>

    <style name="BlackTheme" parent="@android:style/Theme.Black">
    </style>    
</resources>

对可能发生的情况以及如何解决有任何想法吗? 我应该在代码中的任何特殊点调用setTheme 吗?如果有帮助,我的应用程序包含几个活动。

【问题讨论】:

标签: android themes


【解决方案1】:

我也想看看这个方法,你为你的所有活动设置一次。但据我所知,您必须在显示任何视图之前设置每个活动。

作为参考检查:

http://www.anddev.org/applying_a_theme_to_your_application-t817.html

编辑(从那个论坛复制):

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Call setTheme before creation of any(!) View.
         setTheme(android.R.style.Theme_Dark);

        // ...
        setContentView(R.layout.main);
    }


编辑
如果您在super.onCreate(savedInstanceState); 之后调用setTheme,您的活动将重新创建,但如果您在super.onCreate(savedInstanceState); 之前调用setTheme,您的主题将设置和活动 不再重新创建

  protected void onCreate(Bundle savedInstanceState) {
     setTheme(android.R.style.Theme_Dark);
     super.onCreate(savedInstanceState);


    // ...
    setContentView(R.layout.main);
}

【讨论】:

  • 它可以工作,但只有在您没有从一个 Activity 更改为另一个 Activity 时才会应用更改...
  • 请提供一个独立的答案,以便如果该链接失效,人们仍然可以访问您的答案
  • @TashPemhiwa 和​​其他支持该评论的人:自己编辑答案会很困难吗?
  • @Pentium10 没有getApplicationContext().setTheme() 实现“一劳永逸”的活动? (没有测试,但它看起来是个不错的起点)
  • @TWiStErRob 如果没有人说,用户怎么知道要做得更好?
【解决方案2】:

如果您想更改现有活动的主题,请在setTheme() 之后调用recreate()

注意:如果你在onCreate()中更改主题,请不要调用recreate,以免死循环。

【讨论】:

  • 如果它引用一些证据或推理来解释为什么有必要调用recreate(),这个答案会更有用,即使文档(developer.android.com/reference/android/content/…)之前只是说setTheme()任何视图都会被实例化。你是说只有在视图已经实例化时才调用 recreate() 吗?
  • 特别是,如果你在 onCreate() 中调用 setTheme(),在调用 super.onCreate() 或 setContentView() 之前,(1) 你必须检查以防止一个无限循环,你不断地重新创建; (2) 你到底得到了什么?
  • 这非常有用。我已经知道如何在加载之前设置主题,但是为了获得合理的用户体验,立即更改它是必要的。 recreate 正是我所需要的。
  • 不起作用...
【解决方案3】:

recreate()(如TPReal所述)只会重新启动当前活动,但之前的活动仍会在后台堆栈中,并且不会应用主题。

所以,这个问题的另一种解决方案是完全重新创建任务堆栈,如下所示:

    TaskStackBuilder.create(getActivity())
            .addNextIntent(new Intent(getActivity(), MainActivity.class))
            .addNextIntent(getActivity().getIntent())
            .startActivities();

编辑:

在 UI 或其他地方执行主题更改后,只需将上面的代码放在上面。您的所有活动都应该在onCreate() 之前调用方法setTheme(),可能在某些父活动中。将SharedPreferences中选择的主题存储起来,读取,然后使用setTheme()方法设置也是一种正常的做法。

【讨论】:

  • 你会把这段代码放在哪里?如果放在Activity.onCreate()中,会不会导致死循环?
  • 只需在 UI 或其他地方执行主题更改后输入此代码。您的所有活动都应该在onCreate() 之前调用方法setTheme(),可能在某些父活动中。
  • 感谢您的耐心等待我试图理解这一点。我认为“父活动”是指启动(使用startActivity())我想要更改其主题的活动的活动。
  • 不,我所说的父活动是指一些BaseActivity,您的应用中的所有其他活动都可以扩展。
  • 好吧,我猜这段代码应该在BaseActivity的构造函数中,所以它发生在子类活动的onCreate()之前?
【解决方案4】:

我遇到了同样的问题,但我找到了解决方案。

public class EditTextSmartPhoneActivity extends Activity implements DialogInterface.OnClickListener
{
    public final static int CREATE_DIALOG  = -1;
    public final static int THEME_HOLO_LIGHT  = 0;
    public final static int THEME_BLACK  = 1;

    int position;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        position = getIntent().getIntExtra("position", -1);

        switch(position)
        {
        case CREATE_DIALOG:
            createDialog();
            break;
        case THEME_HOLO_LIGHT:
            setTheme(android.R.style.Theme_Holo_Light);
            break;
        case THEME_BLACK:
            setTheme(android.R.style.Theme_Black);
            break;
        default:
        }

        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

    }

    private void createDialog()
    {
        /** Options for user to select*/
        String choose[] = {"Theme_Holo_Light","Theme_Black"};

        AlertDialog.Builder b = new AlertDialog.Builder(this);

        /** Setting a title for the window */
        b.setTitle("Choose your Application Theme");

        /** Setting items to the alert dialog */
        b.setSingleChoiceItems(choose, 0, null);

        /** Setting a positive button and its listener */
        b.setPositiveButton("OK",this);

        /** Setting a positive button and its listener */
        b.setNegativeButton("Cancel", null);

        /** Creating the alert dialog window using the builder class */
        AlertDialog d = b.create();

        /** show dialog*/
        d.show();
    }

    @Override
    public void onClick(DialogInterface dialog, int which) {
        // TODO Auto-generated method stub
        AlertDialog alert = (AlertDialog)dialog;
        int position = alert.getListView().getCheckedItemPosition();

        finish();
        Intent intent = new Intent(this, EditTextSmartPhoneActivity.class);
        intent.putExtra("position", position);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }
}

【讨论】:

  • 这用于当您希望用户在运行时选择主题。无需在清单中设置
  • 这行得通。我将它与 sharedpreferences 配置相结合,因此主题更改是持久的。
  • setTheme() 之后对super.onCreate() 的调用对于完成这项工作也很重要吗?我的印象是,在拨打setContentView()之前,只需拨打setTheme()即可
  • @ErlendK.H.当然。使用共享首选项保存一些东西,maybe an integer。然后下次启动应用程序时,load 保存的值并应用适当的主题。在这个答案中,即onCreate 内的switch(position)。基本上,它与这个答案完全相同,但我还保存了一个值,我在应用程序启动时检查它(并应用必要的主题)。
  • @ErlendK.H.任何保存/加载策略都可以。当时,我使用了共享首选项,因为该项目经常使用它。有些项目有一个设置数据库,所以你也可以使用它。例如,如果您将设置保存在云端,即使 Firebase 也可以。
【解决方案5】:

我遇到了类似的问题,我就这样解决了..

@Override
public void onCreate(Bundle savedInstanceState) {

    if (getIntent().hasExtra("bundle") && savedInstanceState==null){
        savedInstanceState = getIntent().getExtras().getBundle("bundle");
    }

    //add code for theme

    switch(theme)
    {
    case LIGHT:
        setTheme(R.style.LightTheme);
        break;
    case BLACK:
        setTheme(R.style.BlackTheme);
        break;

    default:
    }
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //code

}

此代码用于重新创建 Activity 保存包和更改主题。您必须编写自己的 onSaveInstanceState(Bundle outState);从 API-11 你可以改用 recreate() 方法

Bundle temp_bundle = new Bundle();
onSaveInstanceState(temp_bundle);
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("bundle", temp_bundle);
startActivity(intent);
finish();

【讨论】:

    【解决方案6】:

    我们必须在调用 'super.onCreate()''setContentView()' 之前设置主题> 方法。

    查看此link,了解在运行时将新主题应用于整个应用程序。

    【讨论】:

    • 显然这意味着“在调用 super.onCreate() 和 setContentView() 之前”。顺便说一句,我早些时候否决了这个答案,因为它似乎是错误的;现在我明白了它的意思,并且我看到了它的价值(尽管它没有表达清楚)。不过,除非编辑了答案,否则我无法更改我的投票。如果发生这种情况,请通知我,以便我更改投票。
    • @LarsH 哈哈哈没问题,亲爱的。
    • 我对其进行了编辑并删除了反对票。
    【解决方案7】:

    代替

    getApplication().setTheme(R.style.BlackTheme);
    

    使用

    setTheme(R.style.BlackTheme);
    

    我的代码:在 onCreate() 方法中:

    super.onCreate(savedInstanceState);
    
    if(someExpression) {
        setTheme(R.style.OneTheme);
    } else {
        setTheme(R.style.AnotherTheme);
    }
    
    setContentView(R.layout.activity_some_layout);
    

    某处(例如,单击按钮):

    YourActivity.this.recreate();
    

    您必须重新创建活动,否则 - 不会发生更改

    【讨论】:

      【解决方案8】:

      这是我为 Material Design 创建的。希望对您有所帮助。

      看看MultipleThemeMaterialDesign

      【讨论】:

        【解决方案9】:

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

        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);
        }
        

        【讨论】:

          【解决方案10】:

          这种方式对我有用:

            @Override
          protected void onCreate(Bundle savedInstanceState) {
              setTheme(GApplication.getInstance().getTheme());
              super.onCreate(savedInstanceState);
          
              setContentView(R.layout.activity_main);
          }
          

          那你想换个新主题:

          GApplication.getInstance().setTheme(R.style.LightTheme);
          recreate();
          

          【讨论】:

            【解决方案11】:

            您可以完成 Activity 并在之后重新创建它,这样您的 Activity 将再次创建,并且所有视图都将使用新主题创建。

            【讨论】:

              【解决方案12】:

              在 setTheme() 之后调用 SetContentView(Resource.Layout.Main)。

              【讨论】:

                【解决方案13】:

                这对我没有影响:

                public void changeTheme(int newTheme) {
                    setTheme(newTheme);
                    recreate();
                }
                

                但这有效:

                int theme = R.style.default;
                
                protected void onCreate(Bundle savedInstanceState) {
                    setTheme(this.theme);
                    super.onCreate(savedInstanceState);
                }
                
                public void changeTheme(int newTheme) {
                    this.theme = newTheme;
                    recreate();
                }
                

                【讨论】:

                • 根据您的代码,您将主题设置为一个从未使用过的变量。我相信 onCreate 中的 setTheme 应该是setTheme(theme);
                • 调用recreate()时Activity会被销毁并重新创建,所以这个变量永远不会被使用。只需在定义之前添加 static 即可使其工作。
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2012-05-16
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多