实现用户选择主题

发布于 2024-12-26 07:26:58 字数 419 浏览 4 评论 0原文

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

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

由于这些布局基本相同但颜色不同,因此我希望对每个布局文件中的 TextViews 和其他视图使用相同的 ID。我的主要问题是这会引起问题吗?

抱歉,没有代码的文字墙。我只是想了解针对这种情况的良好实践的一般概念。提前致谢。

I want to give the user the choice between a few different themes, and was wondering if this is an alright way of doing things. I did a little test with this method and it worked, but I think there may be better ways and think it may cause some problems later on, so wanted to ask.

I was thinking of creating a different layout for each theme, and in onCreate just having a switch for the setContentView() method. I'd load a saved SharedPreference value (integer) first and depending on what that value was display the corresponding layout. Obviously the user could change the SharedPreference value with a button or something.

As these layouts would be basically the same but with different colours, I'd want to use the same IDs for my TextViews and other Views in each layout file. My main question is would this cause problems?

Sorry for the wall of text with no code. I'd just like to get a general idea of good practice for this situation. Thanks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

半山落雨半山空 2025-01-02 07:26:58

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

正如已经指出的 - 使用本指南创建一些 Android 主题。 styles.xml 文件中至少有两个

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

现在,您必须将这些样式之一应用到您的活动中。我在 activitie 的 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);

请记住保存对侦听器对象的引用,否则它将被垃圾收集(并且将停止工作)。

I actually have this feature in my application and additionally, I allow users to change theme at runtime. As reading a value from preferences takes some time, I'm getting a theme id via globally accessible function which holds cached value.

As already pointed out - create some Android themes, using this guide. You will have at least two <style> items in your styles.xml file. For example:

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

Now, you have to apply one of these styles to your activities. I'm doing this in activitie's onCreate method, before any other call:

setTheme(MyApplication.getThemeId());

getThemeId is a method which returns cached theme ID:

public static int getThemeId()
{
    return themeId;
}

This field is being updated by another method:

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

Which is being called whenever preferences are changed (and, on startup of course). These two methods reside in MyApplication class, which extends Application. The preference change listener is described at the end of this post and resides in main activity class.

The last and pretty important thing - theme is applied, when an activity starts. Assuming, you can change a theme only in preference screen and that there's only one way of getting there, i.e. from only one (main) activity, this activity won't be restarted when you will exit preference screen - the old theme still will be used. Here's the fix for that (restarts your main activity):

@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 is a boolean variable, initially set to false. It's set to true when theme is changed by this listener, which also updates cached theme ID mentioned before:

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

Remember to hold a reference to the listener object, otherwise it will be garbage colleted (and will cease to work).

再浓的妆也掩不了殇 2025-01-02 07:26:58

如果您使用 Material Components 主题 并遵循浅色和深色主题指南,那么您可以通过 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)

        }
    }

If you are using Material Components themes and followed Light and Dark theme guidelines then you can do it from AppCompatDelegate. These themes can be changed/applied at run time without restarting your application.

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)

        }
    }
世界如花海般美丽 2025-01-02 07:26:58

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

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

在每个活动的 onCreate 之前。

You can also change dynamically theme using:

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

Before onCreate for each activity.

风尘浪孓 2025-01-02 07:26:58

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

为什么不使用样式和主题 功能?

它们可以轻松应用于整个活动:

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

这样,当您检测到您使用的 SharedPreferences 值发生更改(首选项活动上的按钮或其他内容)时,您只需切换样式即可。或者更好的是,您可以设置样式以在运行时(创建活动时)读取您的首选项值,并相应地应用正确的样式/主题。

It does work if you do it this way, and I don't think it would cause any problem, but it seems like a lot of hassle (you have to multiply all your layouts by all the themes you want to add. If later you want to modify a resource in a layout, you'll have to modify it in all the themes. You're definitely bound to forget one)

Why not using the Styles and Themes feature of Android?

They can be applied to the whole activity easily:

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

So that when you detect a change in the SharedPreferences value you use (button on a preference activity, or something) you can just switch the style. Or better, you can set the style to read your preference value at runtime (when creating the activity) and apply the correct style/theme accordingly.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文