java - Substituindo Recursos em Tempo de Execução - Resolvido
java - Substituindo Recursos em Tempo de Execução
Depois de pesquisar por um longo tempo, finalmente encontrei uma excelente solução.
protected void redefineStringResourceId(final String resourceName, final int newId) {
final Field field = R.string.class.getDeclaredField(resourceName);
field.set(null, newId);
Log.e(getClass().getName(), "Couldn't redefine resource id", e);
Para um teste de amostra,
private Object initialStringValue() {
return getString(R.string.initial_value);
E dentro da atividade principal,
before.setText(getString(R.string.before, initialStringValue()));
final String resourceName = getResources().getResourceEntryName(R.string.initial_value);
redefineStringResourceId(resourceName, R.string.evil_value);
after.setText(getString(R.string.after, initialStringValue()));
Esta solução foi originalmente publicada por Roman Zhilich
ResourceHackActivity
Gostaria de substituir os recursos de meus aplicativos, como R.colour.brand_colour ou R.drawable.ic_action_start em tempo de execução. Meu aplicativo se conecta a um sistema CMS que fornecerá cores e imagens de marca. Depois que o aplicativo fizer o download dos dados do CMS, ele precisará ser restaurado.
Eu sei o que você está prestes a dizer - não é possível substituir recursos em tempo de execução.
Exceto que meio que é. Em particular, eu encontrei esta Tese de Bacharel em 2012, que explica o conceito básico - A classe Activity no android estende o ContextWrapper , que contém o método attachBaseContext. Você pode substituir o attachBaseContext para agrupar o Contexto com sua própria classe personalizada, que substitui métodos como getColor e getDrawable. Sua própria implementação de getColor pode procurar a cor da maneira que desejar. A biblioteca de caligrafia usa uma abordagem semelhante para injetar um LayoutInflator personalizado, que pode lidar com o carregamento de fontes personalizadas.
Eu criei uma atividade simples que usa essa abordagem para substituir o carregamento de uma cor.
O problema é que não funciona! O registro mostra chamadas para carregar recursos como layout / activity_main e mipmap / ic_launcher, no entanto, color / theme_colour nunca é carregado. Parece que o contexto está sendo usado para criar a janela e a barra de ação, mas não a exibição do conteúdo da atividade.
Minhas perguntas são - De onde o inflator de layout carrega recursos, se não o contexto das atividades? Eu também gostaria de saber - Existe uma maneira viável de substituir o carregamento de cores e desenhos em tempo de execução?
Uma palavra sobre abordagens alternativas
Eu sei que é possível tema de um aplicativo a partir de dados do CMS de outras maneiras - por exemplo, podemos criar um método getCMSColour(String key) e, em nosso onCreate() , temos um monte de código ao longo das linhas de:
Uma abordagem semelhante pode ser adotada para drawables, strings, etc. No entanto, isso resultaria em uma grande quantidade de código padrão - todos os quais precisam ser mantidos. Ao modificar a interface do usuário, seria fácil esquecer de definir a cor em uma exibição específica.
O agrupamento do contexto para retornar nossos próprios valores personalizados é 'mais limpo' e menos propenso a quebras. Gostaria de entender por que não funciona antes de explorar abordagens alternativas.
Embora "substituir recursos dinamicamente" possa parecer a solução direta para o seu problema, acredito que uma abordagem mais limpa seria usar a implementação oficial de ligação de dados https://developer.android.com/tools/data-binding/guide.html pois não implica invadir a maneira android.
Você pode passar suas configurações de marca usando um POJO. Em vez de usar estilos estáticos como @color/button_color você pode escrever @{brandingConfig.buttonColor} e vincular suas visualizações aos valores desejados. Com uma hierarquia de atividades adequada, não deve adicionar muitos clichês.
Isso também permite alterar elementos mais complexos em seu layout, ou seja: incluir layouts diferentes em outro layout, dependendo das configurações da marca, tornando sua interface do usuário altamente configurável sem muito esforço.