Programmatically change the value of a color resource obtained from API response
Solution 1:
You can create a class which extends Resources
and override the methods getColor(int)
and getColor(int, Theme)
.
Example:
colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="your_special_color">#FF0099CC</color>
</resources>
Res.java
public class Res extends Resources {
public Res(Resources original) {
super(original.getAssets(), original.getDisplayMetrics(), original.getConfiguration());
}
@Override public int getColor(int id) throws NotFoundException {
return getColor(id, null);
}
@Override public int getColor(int id, Theme theme) throws NotFoundException {
switch (getResourceEntryName(id)) {
case "your_special_color":
// You can change the return value to an instance field that loads from SharedPreferences.
return Color.RED; // used as an example. Change as needed.
default:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return super.getColor(id, theme);
}
return super.getColor(id);
}
}
}
BaseActivity.java
public class BaseActivity extends AppCompatActivity {
...
private Res res;
@Override public Resources getResources() {
if (res == null) {
res = new Res(super.getResources());
}
return res;
}
...
}
This is the approach I have used in one of my apps, Root Check. If you override getResources
in your activities and main application class you can change the theme programmatically (even though themes are immutable). If you want, download the app and see how you can set the primary, accent, and background colors from preferences.
Solution 2:
If you take a look at the Accessing Resources document, what it says is that ...
Once you provide a resource in your application, you can apply it by referencing its resource ID. All resource IDs are defined in your project's
R
class, which theaapt
tool automatically generates.
Furthermore,
When your application is compiled,
aapt
generates theR
class, which contains resource IDs for all the resources in yourres/
directory. For each type of resource, there is anR
subclass (for example,R.drawable
for all drawable resources), and for each resource of that type, there is a static integer (for example,R.drawable.icon
). This integer is the resource ID that you can use to retrieve your resource.
What this is saying, essentially, is that pretty much everything held as a resource in the res/
directory is compiled and referenced as an unchangeable constant. It is for this reason that the values of resource elements cannot be changed programmatically/at runtime, because they are compiled. As opposed to local/global variables & SharedPreferences
, resource elements are represented in program memory as fixed, unchangeable objects. They are held in a special read-only region of program memory. In this regard, see also Changing value of R.String Programmatically.
What you can do is, to avoid using the same code at a thousand places in your project, create a common function that changes the value of the color in the SharedPreferences
and use this method everywhere. I'm sure you knew this already, of course.
To reduce the amount of code you need to add to the project, there is an alternative. I have previously used the calligraphy library which allowed me to fix the font style & color throughout the app. This may be of some good use to you, check it out ...
Solution 3:
R
class is not supposed to be edited. It merely contains references to your resources.
You will need to set it manually. However, to reduce the burden of setting it manually you can try to use special libraries for preference saving, for instance:
- Saber - https://github.com/jug6ernaut/saber
- PreferenceBinder - https://github.com/denley/preferencebinder
(full list of similar libraries https://android-arsenal.com/tag/75)
Also, you might want to think about another way of applying styles and passing parameters - consider you would want to add some other parameters like height, width etc. For that purpose, you can define custom attribute in themes.xml/styles.xml:
<attr name="demoColor" format="reference|color" />
then define styles:
<style name="BaseActivity">
</style>
<style name="GreenActivity" parent="@style/BaseActivity">
<item name="demoColor">#00cd00</item>
</style>
<style name="RedActivity" parent="@style/BaseActivity">
<item name="demoColor">#ff0000</item>
</style>
then use that color in your xml like this:
... android:background="?demoColor" ...
and switch between GreenActivity
and RedActivity
styles in Activity.onCreate
:
setTheme(isGreenStyle() ? R.style.GreenActivity : R.style.RedActivity)
setContentView(...)
With the above approach, you will be able to easily configure your styles in xml and it should be less code and easier to refactor in future. (You will still need to have one variable in preference to save whether you have green or red style)
Another way, if you want to show demos of your app with different colors is to use build variants / flavors for loading your app with different colors and styles (it is for build time - not runtime):
app/src/main/res/colors.xml
<resources>
<color name="demoColor">#00cd00</color>
</resources>
app/src/buildVariant/res/colors.xml
<resources>
<color name="demoColor">#ff0000</color>
</resources>
Now you can quickly switch between "main" and "buildVariant" in Build Variants menu and launch your app with different "demo" colors. The same way you can customize a lot of other attributes.
Search for "Build Variants" here http://developer.android.com/tools/building/configuring-gradle.html