Проблема
Я хотел бы иметь возможность переопределять ресурсы моих приложений, такие как R.colour.brand_colour или R.drawable.ic_action_start во время выполнения. Мое приложение подключается к системе CMS, которая будет обеспечивать цвета и изображения брендинга. После того, как приложение загрузило данные CMS, оно должно быть в состоянии перекрасить себя.
Я знаю, что вы собираетесь сказать - переопределение ресурсов во время выполнения невозможно.
Кроме того, что это не так. В частности, я нашел эту Бакалаврскую диссертацию с 2012 года, которая объясняет основную концепцию - класс Activity в android extends ContextWrapper
, который содержит метод attachBaseContext. Вы можете переопределить attachBaseContext, чтобы обернуть контекст своим собственным пользовательским классом, который переопределяет такие методы, как getColor и getDrawable. Ваша собственная реализация getColor может выглядеть так, как хотелось бы. Библиотека Каллиграфия использует аналогичный подход для ввода пользовательского LayoutInflator, который может работать с загрузкой пользовательских шрифтов.
Код
Я создал простую операцию, которая использует этот подход для переопределения загрузки цвета.
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, однако цвет/тема_цвет никогда не загружается. Кажется, что контекст используется для создания окна и панели действий, но не для представления содержимого активности.
Мои вопросы - Где загрузочный ресурс компоновщика компоновки, если не контекст действий? Я также хотел бы знать - Существует ли работоспособный способ переопределить загрузку цветов и чертежи во время выполнения?
Слово об альтернативных подходах
Я знаю, что его можно использовать для приложения из данных CMS другими способами - например, мы могли бы создать метод getCMSColour(String key)
, тогда внутри нашего onCreate()
у нас есть куча кода по строкам:
myTextView.setTextColour(getCMSColour("heading_text_colour"))
Аналогичный подход можно было бы использовать для чертежей, строк и т.д. Однако это привело бы к большому количеству кода шаблона, все из которых нуждается в поддержании. При изменении пользовательского интерфейса было бы легко забыть установить цвет в определенном виде.
Обтекание контекста для возврата наших собственных значений является "более чистым" и менее подверженным поломке. Я хотел бы понять, почему он не работает, прежде чем исследовать альтернативные подходы.