我想让用户在几个不同的主题之间做出选择,并且想知道这是否是一种正常的做事方式。我用这种方法进行了一些测试并且它有效,但我认为可能有更好的方法,并认为它可能会在以后引起一些问题,所以想问一下。
我正在考虑为每个主题创建不同的布局,并且onCreate
只需要切换setContentView()
方法。我首先加载一个保存的SharedPreference
值(整数),具体取决于显示相应布局的值。显然,用户可以使用按钮或其他内容更改SharedPreference
值。
由于这些布局基本相同但颜色不同,我想在每个布局文件中为TextViews
和其他视图使用相同的ID。我的主要问题是这会导致问题吗?
抱歉没有代码的文字墙。我想对这种情况大致了解良好做法。提前谢谢。
答案 0 :(得分:43)
我实际上在我的应用程序中有这个功能,另外,我允许用户在运行时更改主题。从首选项中读取值需要一些时间,我通过全局可访问函数获取主题ID,该函数包含缓存值。
正如已经指出的那样 - 使用this guide创建一些Android主题。您的<style>
文件中至少有两个styles.xml
项。例如:
<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);
请记住持有对侦听器对象的引用,否则将被垃圾收集(并将停止工作)。
答案 1 :(得分:3)
您还可以使用以下方式动态更改主题:
ContextThemeWrapper w = new ContextThemeWrapper(this, <newTHEMEId>);
getTheme().setTo(w.getTheme());
在onCreate之前进行每项活动。
答案 2 :(得分:3)
如果您使用的是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 :(得分:2)
如果你这样做,它确实有效,我认为这不会引起任何问题,但似乎有很多麻烦(你必须将你想要添加的所有主题的所有布局相乘。如果以后要修改布局中的资源,则必须在所有主题中修改它。你肯定会忘记一个)
为什么不使用Android的Styles and Themes功能?
它们可以轻松应用于整个活动:
<activity android:theme="@style/my_theme">
因此,当您检测到您使用的SharedPreferences
值的更改(首选项活动上的按钮或其他内容)时,您只需切换样式即可。或者更好的是,您可以将样式设置为在运行时(创建活动时)读取您的首选项值并相应地应用正确的样式/主题。