动态设置主题颜色
Set theme color dynamically
我在我的 android 应用程序中(动态地)使用主题,如下所示:
my_layout.xml(摘录):
<TextView
android:id="@+id/myItem"
style="?my_item_style" />
attrs.xml(摘录):
<attr name="my_item_style" format="reference" />
themes.xml(摘录):
<style name="MainTheme.Blue">
<item name="my_item_style">@style/my_item_style_blue</item>
</style>
<style name="MainTheme.Green">
<item name="my_item_style">@style/my_item_style_green<item>
</style>
styles.xml(摘录):
<style name="my_item_style_blue">
<item name="android:textColor">@color/my_blue</item>
</style>
<style name="my_item_style_green">
<item name="android:textColor">@color/my_blue</item>
</style>
因此,如您所见,我正在动态设置主题。我正在使用这个 class:
public class ThemeUtils {
private static int sTheme;
public final static int THEME_BLUE = 1;
public final static int THEME_GREEN = 2;
public static void changeToTheme(MainActivity activity, int theme) {
sTheme = theme;
activity.startActivity(new Intent(activity, MyActivity.class));
}
public static void onActivityCreateSetTheme(Activity activity)
{
switch (sTheme)
{
default:
case THEME_DEFAULT:
case THEME_BLUE:
activity.setTheme(R.style.MainTheme_Blue);
break;
case THEME_GREEN:
activity.setTheme(R.style.MainTheme_Green);
break;
}
}
}
我想知道的是,有没有办法在代码中做到这一点(更改主题颜色)?例如,我有以下代码(摘录):
((TextView) findViewById(R.id.myItem)).setTextColor(R.color.blue);
这可以通过一些辅助方法来完成,该方法将对可用主题使用 switch
命令,对主题使用 return 正确颜色。但是我想知道有没有更好更好更快的方法
谢谢!
如果我正确理解你正在寻找一种方法来
- 从主题中提取样式,
- 从所述样式中提取一个值(文本颜色)。
让我们开始吧。
// Extract ?my_item_style from a context/activity.
final TypedArray a = context.obtainStyledAttributes(new int[] { R.attr.my_item_style });
@StyleRes final int styleResId = a.getResourceId(0, 0);
a.recycle();
// Extract values from ?my_item_style.
final TypedArray b = context.obtainStyledAttributes(styleResId, new int[] { android.R.attr.textColor });
final ColorStateList textColors = b.getColorStateList(0);
b.recycle();
// Apply extracted values.
if (textColors != null) {
textView.setTextColor(textColors);
}
一些注意事项:
TypedArray
不支持在较旧的 API 关卡的颜色状态列表中获取支持矢量可绘制对象和主题参考。如果您愿意使用 AppCompat internal API,您可能想尝试 TintTypedArray
.
- 一直分配
int[]
成本很高,让它成为 static final
。
- 如果您想一次解析多个属性,则必须对属性数组进行排序!否则它有时会崩溃。
<declare-styleable>
为您生成这样的数组和相应的索引。
通过 Intent
传递主题 ID 怎么样?
Intent intent = new Intent(activity, MyActivity.class);
intent.putExtra("theme", R.style.MainTheme_Green);
activity.startActivity(intent);
然后在onCreate
:
// assuming that MainTheme_Blue is default theme
setTheme(getIntent().getIntExtra("theme", R.style.MainTheme_Blue));
你检查过这个 MultipleThemeMaterialDesign 演示了吗?
设置活动:
@Override
protected void onCreate(Bundle savedInstanceState) {
Preferences.applyTheme(this);
getDelegate().installViewFactory();
getDelegate().onCreate(savedInstanceState);
super.onCreate(savedInstanceState);
setToolbar();
addPreferencesFromResource(R.xml.preferences);
Preferences.sync(getPreferenceManager());
mListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Preferences.sync(getPreferenceManager(), key);
if (key.equals(getString(R.string.pref_theme))) {
finish();
final Intent intent = IntentCompat.makeMainActivity(new ComponentName(
SettingsActivity.this, MainActivity.class));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | IntentCompat.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
}
};
}
查看演示的完整示例。
我终于用下面的方法完成了:
public static int getColor(String colorName) {
Context ctx = getContext();
switch (sTheme) {
default:
case THEME_DEFAULT:
return ctx.getResources().getIdentifier("BLUE_" + colorName, "color", ctx.getPackageName());
case THEME_BLUE:
return ctx.getResources().getIdentifier("BLUE_" + colorName, "color", ctx.getPackageName());
case THEME_GREEN:
return ctx.getResources().getIdentifier("GREEN_" + colorName, "color", ctx.getPackageName());
}
}
这个 returns 颜色根据我的主题(我使用了前缀)。
鉴于每个资源都是 R class 中的一个字段,您可以使用反射查找它们。这很昂贵,但由于您将获得一个 int 值,因此您可以在获得它们后存储它们并避免性能下降。并且由于使用资源的方法采用任何int,您可以使用一个int变量作为占位符,然后将所需的颜色放入其中。
获得任何资源:
String awesomeColor = "blue";
int color = getResourceId(R.color, awesomeColor, false);
if(blue>0) ((TextView) findViewById(R.id.myItem)).setTextColor(color);
函数:
public static int getResourceId(Class rClass, String resourceText, boolean showExceptions){
String key = rClass.getName()+"-"+resourceText;
if(FailedResourceMap.containsKey(key)) return 0;
if(ResourceMap.containsKey(key)) return ResourceMap.get(rClass.getName()+"-"+resourceText);
try {
String originalText = resourceText;
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.GINGERBREAD){
resourceText = ValidationFunctions.normalizeText(resourceText);
}
resourceText = resourceText.replace("?", "").replace(" ", " ").replace(" ", "_").replace("(", "").replace(")", "");
int resource = rClass.getDeclaredField(resourceText).getInt(null);
ResourceMap.put(rClass.getName()+"-"+originalText, resource);
return resource;
} catch (IllegalAccessException | NullPointerException e) {
FailedResourceMap.put(key, 0);
if(showExceptions) e.printStackTrace();
} catch (NoSuchFieldException e) {
FailedResourceMap.put(key, 0);
if(showExceptions) e.printStackTrace();
}
return 0;
}
此处理对任何 android 资源都有效。你也可以这样设置主题而不是使用中间变量:
public static void onActivityCreateSetTheme(Activity activity)
{
int theme = getResourceId(R.style, activity.getClass().getSimpleName(), false);
if(theme > 0) activity.setTheme(theme);
}
我在我的 android 应用程序中(动态地)使用主题,如下所示:
my_layout.xml(摘录):
<TextView
android:id="@+id/myItem"
style="?my_item_style" />
attrs.xml(摘录):
<attr name="my_item_style" format="reference" />
themes.xml(摘录):
<style name="MainTheme.Blue">
<item name="my_item_style">@style/my_item_style_blue</item>
</style>
<style name="MainTheme.Green">
<item name="my_item_style">@style/my_item_style_green<item>
</style>
styles.xml(摘录):
<style name="my_item_style_blue">
<item name="android:textColor">@color/my_blue</item>
</style>
<style name="my_item_style_green">
<item name="android:textColor">@color/my_blue</item>
</style>
因此,如您所见,我正在动态设置主题。我正在使用这个 class:
public class ThemeUtils {
private static int sTheme;
public final static int THEME_BLUE = 1;
public final static int THEME_GREEN = 2;
public static void changeToTheme(MainActivity activity, int theme) {
sTheme = theme;
activity.startActivity(new Intent(activity, MyActivity.class));
}
public static void onActivityCreateSetTheme(Activity activity)
{
switch (sTheme)
{
default:
case THEME_DEFAULT:
case THEME_BLUE:
activity.setTheme(R.style.MainTheme_Blue);
break;
case THEME_GREEN:
activity.setTheme(R.style.MainTheme_Green);
break;
}
}
}
我想知道的是,有没有办法在代码中做到这一点(更改主题颜色)?例如,我有以下代码(摘录):
((TextView) findViewById(R.id.myItem)).setTextColor(R.color.blue);
这可以通过一些辅助方法来完成,该方法将对可用主题使用 switch
命令,对主题使用 return 正确颜色。但是我想知道有没有更好更好更快的方法
谢谢!
如果我正确理解你正在寻找一种方法来
- 从主题中提取样式,
- 从所述样式中提取一个值(文本颜色)。
让我们开始吧。
// Extract ?my_item_style from a context/activity.
final TypedArray a = context.obtainStyledAttributes(new int[] { R.attr.my_item_style });
@StyleRes final int styleResId = a.getResourceId(0, 0);
a.recycle();
// Extract values from ?my_item_style.
final TypedArray b = context.obtainStyledAttributes(styleResId, new int[] { android.R.attr.textColor });
final ColorStateList textColors = b.getColorStateList(0);
b.recycle();
// Apply extracted values.
if (textColors != null) {
textView.setTextColor(textColors);
}
一些注意事项:
TypedArray
不支持在较旧的 API 关卡的颜色状态列表中获取支持矢量可绘制对象和主题参考。如果您愿意使用 AppCompat internal API,您可能想尝试TintTypedArray
.- 一直分配
int[]
成本很高,让它成为static final
。 - 如果您想一次解析多个属性,则必须对属性数组进行排序!否则它有时会崩溃。
<declare-styleable>
为您生成这样的数组和相应的索引。
通过 Intent
传递主题 ID 怎么样?
Intent intent = new Intent(activity, MyActivity.class);
intent.putExtra("theme", R.style.MainTheme_Green);
activity.startActivity(intent);
然后在onCreate
:
// assuming that MainTheme_Blue is default theme
setTheme(getIntent().getIntExtra("theme", R.style.MainTheme_Blue));
你检查过这个 MultipleThemeMaterialDesign 演示了吗?
设置活动:
@Override
protected void onCreate(Bundle savedInstanceState) {
Preferences.applyTheme(this);
getDelegate().installViewFactory();
getDelegate().onCreate(savedInstanceState);
super.onCreate(savedInstanceState);
setToolbar();
addPreferencesFromResource(R.xml.preferences);
Preferences.sync(getPreferenceManager());
mListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Preferences.sync(getPreferenceManager(), key);
if (key.equals(getString(R.string.pref_theme))) {
finish();
final Intent intent = IntentCompat.makeMainActivity(new ComponentName(
SettingsActivity.this, MainActivity.class));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | IntentCompat.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
}
};
}
查看演示的完整示例。
我终于用下面的方法完成了:
public static int getColor(String colorName) {
Context ctx = getContext();
switch (sTheme) {
default:
case THEME_DEFAULT:
return ctx.getResources().getIdentifier("BLUE_" + colorName, "color", ctx.getPackageName());
case THEME_BLUE:
return ctx.getResources().getIdentifier("BLUE_" + colorName, "color", ctx.getPackageName());
case THEME_GREEN:
return ctx.getResources().getIdentifier("GREEN_" + colorName, "color", ctx.getPackageName());
}
}
这个 returns 颜色根据我的主题(我使用了前缀)。
鉴于每个资源都是 R class 中的一个字段,您可以使用反射查找它们。这很昂贵,但由于您将获得一个 int 值,因此您可以在获得它们后存储它们并避免性能下降。并且由于使用资源的方法采用任何int,您可以使用一个int变量作为占位符,然后将所需的颜色放入其中。
获得任何资源:
String awesomeColor = "blue";
int color = getResourceId(R.color, awesomeColor, false);
if(blue>0) ((TextView) findViewById(R.id.myItem)).setTextColor(color);
函数:
public static int getResourceId(Class rClass, String resourceText, boolean showExceptions){
String key = rClass.getName()+"-"+resourceText;
if(FailedResourceMap.containsKey(key)) return 0;
if(ResourceMap.containsKey(key)) return ResourceMap.get(rClass.getName()+"-"+resourceText);
try {
String originalText = resourceText;
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.GINGERBREAD){
resourceText = ValidationFunctions.normalizeText(resourceText);
}
resourceText = resourceText.replace("?", "").replace(" ", " ").replace(" ", "_").replace("(", "").replace(")", "");
int resource = rClass.getDeclaredField(resourceText).getInt(null);
ResourceMap.put(rClass.getName()+"-"+originalText, resource);
return resource;
} catch (IllegalAccessException | NullPointerException e) {
FailedResourceMap.put(key, 0);
if(showExceptions) e.printStackTrace();
} catch (NoSuchFieldException e) {
FailedResourceMap.put(key, 0);
if(showExceptions) e.printStackTrace();
}
return 0;
}
此处理对任何 android 资源都有效。你也可以这样设置主题而不是使用中间变量:
public static void onActivityCreateSetTheme(Activity activity)
{
int theme = getResourceId(R.style, activity.getClass().getSimpleName(), false);
if(theme > 0) activity.setTheme(theme);
}