在运行时覆盖资源 [英] Overriding resources at runtime

查看:50
本文介绍了在运行时覆盖资源的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望能够在运行时覆盖我的应用程序资源,例如R.colour.brand_colour或R.drawable.ic_action_start.我的应用程序连接到CMS系统,该系统将提供商标颜色和图像.该应用下载完CMS数据后,就需要能够重新设置自身外观.

I would like to be able to override my apps resources such as R.colour.brand_colour or R.drawable.ic_action_start at runtime. My application connects to a CMS system that will provide branding colours and images. Once the app has downloaded the CMS data it needs to be able to re-skin itself.

我知道您要说的是-在运行时无法覆盖资源.

I know what you are about to say - overriding resources at runtime is not possible.

除了有点.特别是,我发现了2012年的 Bachelor Thesis ,它解释了基本概念-android中的Activity类扩展了 ContextWrapper ,其中包含attachBaseContext方法.您可以重写attachBaseContext,以用您自己的自定义类包装Context,该自定义类将覆盖诸如getColor和getDrawable之类的方法.您自己的getColor实现可以根据需要查找颜色.书法库使用类似的方法来注入可处理加载自定义字体的自定义LayoutInflator.

Except that it kinda is. In particular I have found this Bachelor Thesis from 2012 which explains the basic concept - The Activity class in android extends ContextWrapper, which contains the attachBaseContext method. You can override attachBaseContext to wrap the Context with your own custom class which overrides methods such as getColor and getDrawable. Your own implementation of getColor could look the colour up however it wanted. The Calligraphy library uses a similar approach to inject a custom LayoutInflator which can deal with loading custom fonts.

我创建了一个简单的Activity,使用此方法来覆盖颜色的加载.

I have created a simple Activity which uses this approach to override the loading of a colour.

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.似乎上下文用于创建窗口和操作栏,而不是用于创建活动的内容视图.

The problem is, it doesn't work! The logging shows calls to load resources such as layout/activity_main and mipmap/ic_launcher however color/theme_colour is never loaded. It seems that the context is being used to create the window and action bar, but not the activity's content view.

我的问题是-如果不是活动上下文,布局充气机将从何处加载资源?我还想知道-是否有一种可行的方法可以覆盖颜色的加载和可绘制对象在运行时?

My questions is - Where does the layout inflator load resources from, if not the activities context? I would also like to know - Is there a workable way to override the loading of colours and drawables at runtime?

我知道可以通过其他方式从CMS数据中为应用程序主题化-例如,我们可以创建方法 getCMSColour(String key),然后在我们的 onCreate()内部遵循以下方式编写了一堆代码:

I know its possible to theme an app from CMS data other ways - for example we could create a method getCMSColour(String key) then inside our onCreate() we have a bunch of code along the lines of:

myTextView.setTextColour(getCMSColour("heading_text_colour"))

对于可绘制对象,字符串等可以采用类似的方法.但是,这将导致大量的样板代码-所有这些都需要维护.修改UI时,很容易忘记在特定视图上设置颜色.

A similar approach could be taken for drawables, strings, etc. However this would result in a large amount of boilerplate code - all of which needs maintaining. When modifying the UI it would be easy to forget to set the colour on a particular view.

包装上下文以返回我们自己的自定义值比较干净",并且不易损坏.在探索替代方法之前,我想了解为什么它不起作用.

Wrapping the Context to return our own custom values is 'cleaner' and less prone to breakage. I would like to understand why it doesn't work, before exploring alternative approaches.

推荐答案

尽管动态覆盖资源"似乎是解决您问题的直接方法,但我认为更干净的方法是使用官方数据绑定实现https://developer.android.com/tools/data-binding/guide.html 暗示 hacking 是android方式.

While "dynamically overriding resources" might seem the straightforward solution to your problem, I believe a cleaner approach would be to use the official data binding implementation https://developer.android.com/tools/data-binding/guide.html since it doesn't imply hacking the android way.

您可以使用POJO传递品牌设置.除了使用 @ color/button_color 之类的静态样式外,您还可以编写 @ {brandingConfig.buttonColor} 并将视图绑定到所需的值.具有适当的活动层次结构,它不应添加过多的样板.

You could pass your branding settings using a POJO. Instead of using static styles like @color/button_color you could write @{brandingConfig.buttonColor} and bind your views with the desired values. With a proper activity hierarchy, it shouldn't add too much boilerplate.

这还使您能够更改布局上更复杂的元素,即:根据品牌设置在其他布局上包括不同的布局,从而使您的UI高度可配置,而无需花费太多精力.

This also gives you the ability to change more complex elements on your layout, i.e.: including different layouts on other layout depending on the branding settings, making your UI highly configurable without too much effort.

这篇关于在运行时覆盖资源的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆