Android: 如何获得当前主题的资源 ID?

在 Android 中,您可以从 getTheme()获得活动的当前主题作为 Resource.Theme对象。此外,还可以通过另一个主题的资源 ID 将主题设置为不同的主题,如 setTheme(R.style.Theme_MyTheme)中所示。

但是我怎样才能知道它是否值得——当前的主题是否已经是我想要设置的主题?我正在寻找像 getTheme().getResourceId()这样的东西,为了写一些像:

protected void onResume() {
int newThemeId = loadNewTheme();
if (newThemeId != getTheme().getResourceId()) { // !!!! How to do this?
setTheme(newThemeId);
// and rebuild the gui, which is expensive
}
}

有什么想法吗?

41490 次浏览

OK here's one puzzle piece: we can get the default theme, as set in the AndroidManifest.xml, as context.getApplicationInfo().theme for the theme set at application level, and from within an Activity, as getPackageManager().getActivityInfo(getComponentName(), 0).theme for that activity.

I guess that gives us a starting point to do our own wrapper for a custom getTheme() and setTheme().

Still that feels like working around rather than with the API. So I'll leave the question open to see if someone comes up with a better idea.

EDIT: There's

getPackageManager().getActivityInfo(getComponentName(), 0).getThemeResource()

which will automatically fallback to application theme if the activity doesn't override it.

I found a way to solve the requirement without getting the resource id.

I'm adding an item to each of my themes with the name of the string:

<item name="themeName">dark</item>

And in the code I check the name like so:

TypedValue outValue = new TypedValue();
getTheme().resolveAttribute(R.attr.themeName, outValue, true);
if ("dark".equals(outValue.string)) {
...
}

There is a way to do this via reflection. Put this in your Activity:

int themeResId = 0;
try {
Class<?> clazz = ContextThemeWrapper.class;
Method method = clazz.getMethod("getThemeResId");
method.setAccessible(true);
themeResId = (Integer) method.invoke(this);
} catch (NoSuchMethodException e) {
Log.e(TAG, "Failed to get theme resource ID", e);
} catch (IllegalAccessException e) {
Log.e(TAG, "Failed to get theme resource ID", e);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Failed to get theme resource ID", e);
} catch (InvocationTargetException e) {
Log.e(TAG, "Failed to get theme resource ID", e);
}
// use themeResId ...

[Insert disclaimer here regarding non-public apis]

According to sources Activity.setTheme is called before Activity.onCreate, so you can save the themeId when android set it:

public class MainActivity extends Activity {
private int themeId;


@Override
public void setTheme(int themeId) {
super.setTheme(themeId);
this.themeId = themeId;
}


public int getThemeId() {
return themeId;
}
}

If you have specified android:theme="@style/SomeTheme" in the <activity/> element in your manifest, then you can get the resource ID of the activity's theme like this:

int themeResId;
try {
PackageManager packageManager = getPackageManager();
//ActivityInfo activityInfo = packageManager.getActivityInfo(getCallingActivity(), PackageManager.GET_META_DATA);
ActivityInfo activityInfo = packageManager.getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
themeResId = activityInfo.theme;
}
catch(PackageManager.NameNotFoundException e) {
Log.e(LOG_TAG, "Could not get themeResId for activity", e);
themeResId = -1;
}

Don't forget, if you are going to then call setTheme(themeResId) in your activity's onCreate() method, you need to do it before you call setContentView(...).