【发布时间】:2015-08-11 21:56:08
【问题描述】:
问题
我希望能够在运行时覆盖我的应用资源,例如 R.colour.brand_colour 或 R.drawable.ic_action_start。我的应用程序连接到将提供品牌颜色和图像的 CMS 系统。一旦应用下载了 CMS 数据,它就需要能够重新换肤。
我知道你要说什么 - 在运行时覆盖资源是不可能的。
除了它有点像。特别是我从 2012 年发现了这个 Bachelor Thesis,它解释了基本概念 - android 中的 Activity 类扩展了 ContextWrapper,其中包含 attachBaseContext 方法。您可以重写 attachBaseContext 以使用您自己的自定义类包装 Context,该类会重写 getColor 和 getDrawable 等方法。您自己的 getColor 实现可以根据需要查找颜色。 Calligraphy library 使用类似的方法来注入一个自定义的 LayoutInflator,它可以处理加载自定义字体。
代码
我创建了一个简单的 Activity,它使用这种方法来覆盖颜色的加载。
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(new CmsThemeContextWrapper(newBase));
}
private class CmsThemeContextWrapper extends ContextWrapper{
private Resources resources;
public CmsThemeContextWrapper(Context base) {
super(base);
resources = new Resources(base.getAssets(), base.getResources().getDisplayMetrics(), base.getResources().getConfiguration()){
@Override
public void getValue(int id, TypedValue outValue, boolean resolveRefs) throws NotFoundException {
Log.i("ThemeTest", "Getting value for resource " + getResourceName(id));
super.getValue(id, outValue, resolveRefs);
if(id == R.color.theme_colour){
outValue.data = Color.GREEN;
}
}
@Override
public int getColor(int id) throws NotFoundException {
Log.i("ThemeTest", "Getting colour for resource " + getResourceName(id));
if(id == R.color.theme_colour){
return Color.GREEN;
}
else{
return super.getColor(id);
}
}
};
}
@Override
public Resources getResources() {
return resources;
}
}
}
问题是,它不起作用!日志显示加载资源的调用,例如 layout/activity_main 和 mipmap/ic_launcher,但是 color/theme_colour 从未加载。似乎上下文被用于创建窗口和操作栏,而不是活动的内容视图。
我的问题是 - 布局充气器从哪里加载资源,如果不是活动上下文?我也想知道 - 是否有一种可行的方法来覆盖颜色的加载和运行时的可绘制对象?
关于替代方法的一句话
我知道可以通过其他方式从 CMS 数据中为应用设置主题 - 例如,我们可以创建一个方法 getCMSColour(String key),然后在我们的 onCreate() 中,我们有一堆代码如下:
myTextView.setTextColour(getCMSColour("heading_text_colour"))
类似的方法可以用于可绘制对象、字符串等。但这会导致大量样板代码——所有这些都需要维护。在修改 UI 时,很容易忘记在特定视图上设置颜色。
包装 Context 以返回我们自己的自定义值更“干净”且不易损坏。在探索替代方法之前,我想了解它为什么不起作用。
【问题讨论】:
-
您的解决方案有效:在活动中,如果您调用 getResources().getColor(R.color.theme_colour) 结果是 Color.GREEN 正如预期的那样。充气机似乎使用了另一种方法来检索颜色,我不知道是哪一种。我尝试包装应用程序上下文,但结果相同...
-
是的,我知道调用 getResource().getColour() 将返回绿色。但是我的问题是,当布局膨胀时,为什么我设置的控件不是 android:colour="@color/theme_colour" green!
-
不是您问题的答案(实际上,如果可能的话,我会非常感兴趣),但作为另一种替代方法,您可以自己覆盖使用的小部件(TextView、ImageView 等)您自己的“资源提供者”实现(您已在“替代方法”段落中添加并在您的视图中使用它。这样,您可以减少样板代码的数量,并且更容易维护主题。至少,如果所有其他方法都失败了,我个人会采用这种方法,而不是覆盖每个活动/片段中的主题和资源。
-
@kha 在这里有一个合理的方法。除此之外,没有一种可靠的方法可以用任意数据动态替换从 XML 引用的值。任何涉及反射的方法都会破坏您的应用程序(我们看到很多这种情况是由于 M 中的资源框架更改)。
-
啊,我明白了。我为我之前的困惑道歉。在一个理想的世界里,你的方法会奏效。在一个理想的世界里,我会有头发。这不是一个理想的世界,更可惜的是。
标签: java android android-activity