以编程方式更改从 API 响应获得的颜色资源的值

Programmatically change the value of a color resource obtained from API response

比方说,在我的 API 调用中,我有一个名为 color 的参数。是否可以编辑或修改现有的 R.colors.color 以从 API 结果分配颜色?

举个例子:

我打电话给我的 API 并且它 returns green,现在我想加载我的应用程序,即 (green Toolbar, green TextView颜色等),可以吗?

我的第一个想法是:

colors.xml 上创建一个名为 demo 的项目,然后为其指定默认颜色,然后在任何我想要的地方使用此 demo 颜色(ButtonTextView,等等)然后我认为可以使用 API 的结果以编程方式更改此值,这样我就不需要创建 SharedPreferences 或类似的东西并避免更多代码.

正如@Y.S.对我说的

Unfortunately, you WILL have to set the color of the text or view manually everywhere ... :(

我想知道是否有其他方法可以做到这一点,因为我不知道我的项目将包含多少 Activities,所以如果有其他方法可以做到这一点,我很高兴听到其他猜测。

编辑

我正在尝试@Jared Rummler 的回答,也许我做错了什么...我创建了一个简单的 Json 并放置了我的资产,我解析了 Json我把它放在 GlobalConstant 然后我做了 "simple app".

首先我有一个 TextView 和一个包含 "your_special_color" 的 Button 和其中的 return 我把 GlobalConstant int 作为如下:

case "your_special_color":                
            return GlobalConstant.color; 

然后我尝试的是我的第一个 Activity 有 1 个 TextView 和 1 个 Button 正如我之前所说,它们有我没有的颜色 "your_special_color"想更改它,但我的 Button 上有一个 Intent 可以打开另一个 Activity,其中包含相同但带有 GlobalConstant.color 并且它没有改变。

我尝试这样做(我的第二个 Activity):

public class Main2Activity extends AppCompatActivity {
private Res res;
@Override public Resources getResources() {
    if (res == null) {
        res = new Res(super.getResources());
    }
    return res;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main2);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);
}

我是不是漏掉了什么?

哦..我想通了我猜是在我的 MainActivity2 上这样做吗?

 Button btn = (Button)findViewById(R.id.button2);
 btn.setBackgroundColor(res.getColor(R.color.your_special_color));

您无法更改应用的资源,它们都是常量。相反,您可以将颜色保存在 SharedPrefences 中并使用那里的颜色。

参见How to use SharedPreferences in Android to store, fetch and edit values

如果您的应用已经定义了 R.color.green,而您只想根据 API 返回的内容访问它,请使用:

int resourceID = getResources().getIdentifier("green", "color", getPackageName());

如果你看一下 Accessing Resources 文档,它说的是......

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 the aapt tool automatically generates.

此外,

When your application is compiled, aapt generates the R class, which contains resource IDs for all the resources in your res/ directory. For each type of resource, there is an R 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.

这基本上是说 res/ 目录中几乎所有作为 资源 保存的内容都被编译并引用为不可更改的常量。正是由于这个原因,资源元素的值无法在运行时更改programmatically/at,因为它们是编译。与 local/global 变量和 SharedPreferences 相反,资源元素在程序内存中表示为固定的、不可更改的对象。它们保存在程序存储器的一个特殊的只读区域中。在这方面,另请参阅 Changing value of R.String Programmatically.

可以做的是,为了避免在项目中的一千个地方使用相同的代码,创建一个通用函数来更改 SharedPreferences 到处使用这个方法。当然,我相信你已经知道了。

要减少需要添加到项目中的代码量,有一个替代方法。我之前使用过 calligraphy library,它允许我在整个应用程序中修复字体样式和颜色。这可能对你有用,看看吧...

将十六进制颜色代码存储到 sharedpreferences 中,然后使用 parsecolor 函数将所有十六进制颜色代码作为字符串存储到会话中,并且每当您想要更改特定按钮、文本视图的颜色时,只需从会话中检索该颜色代码并使用它作为
例如
session.setString("white","#FFFFFF"); String colorname=session.getString("white");yourtextview.setBackgroundColor(Color.parseColor(colorname);

您可以创建一个 class 来扩展 Resources 并覆盖方法 getColor(int)getColor(int, Theme).

示例:

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

    ...

}

这是我在我的一个应用程序 Root Check 中使用的方法。如果您在活动和主应用程序 class 中覆盖 getResources,您可以通过编程方式更改主题(即使主题是不可变的)。如果需要,请下载该应用程序并查看如何根据首选项设置主色、强调色和背景色。

R class 不应该被编辑。它仅包含对您的资源的引用。

您需要手动设置。但是,为了减少手动设置的负担,您可以尝试使用特殊的库来保存首选项,例如:

(类似库的完整列表 https://android-arsenal.com/tag/75


此外,您可能想考虑另一种应用样式和传递参数的方式 - 考虑您想要添加一些其他参数,如高度、宽度等。为此,您可以在主题中定义自定义属性。xml/styles.xml:

<attr name="demoColor" format="reference|color" />

然后定义样式:

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

然后像这样在 xml 中使用该颜色:

... android:background="?demoColor" ...

并在 Activity.onCreate 中的 GreenActivityRedActivity 样式之间切换:

setTheme(isGreenStyle() ? R.style.GreenActivity : R.style.RedActivity)
setContentView(...)

通过上述方法,您将能够轻松地在 xml 中配置您的样式,并且代码应该更少,并且将来更容易重构。 (你仍然需要有一个变量来保存你是绿色还是红色风格)


另一种方法,如果你想用不同的颜色展示你的应用程序的演示,是使用构建变体/风格来加载你的具有不同颜色和样式的应用程序(它是针对构建时间 - 而不是运行时):

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>

现在您可以在 Build Variants 菜单中在 "main" 和 "buildVariant" 之间快速切换,并使用不同的 "demo" 颜色启动您的应用程序。同样的方法你可以自定义很多其他的属性。

在此处搜索 "Build Variants" http://developer.android.com/tools/building/configuring-gradle.html