Cuerdas Android dependientes del tema

Digamos que tengo una aplicación con dos temas: masculino y femenino. Los temas simplemente cambian la paleta de colores y unos pocos dibujos para apelar a los gustos preferidos del usuario.

Muchas gracias a http://www.androidengineer.com/2010/06/using-themes-in-android-applications.html por sus consejos para hacer que funcione.

Pero ahora vamos a decir que quiero conseguir un poco más linda con la aplicación y no sólo cambiar los colores y drawables, pero también quiero cambiar las cadenas. Por ejemplo, yo podría querer añadir un tema de pirata y luego "Enviar" sería "Arrrrgh!"

Por lo tanto, mi pregunta básica es: ¿Cómo puedo cambiar las cadenas a través de mi aplicación a través de temas seleccionables por el usuario?

Editar:

Haciendo esto: la aplicación tiene 12 botones y 32 vistas de texto Me gustaría tener tema dependiente y me gustaría lograr esto sin una cartografía gigante o una gran cantidad de attrs personalizado.

Las 3 soluciones actuales funcionarán. Buscando algo más limpio aunque no sé que tal bestia existe.

Digamos que tengo una aplicación con dos temas: masculino y femenino. Los temas simplemente cambian la paleta de colores y unos pocos dibujos para apelar a los gustos preferidos del usuario.

¿Qué tal fingir que estás haciendo otra cosa? Esto es un diseño anti-patrón, asociando colores particulares basados ​​en género (eg, "las muchachas tienen gusto de rosado").

Esto no quiere decir que su objetivo técnico es malo, sólo que este es un ejemplo realmente estereotípico.

Por ejemplo, yo podría querer añadir un tema de pirata y luego "Enviar" sería "Arrrrgh!"

Sólo si "Cancelar" se asigna a "Avast!".

¿Cómo puedo cambiar las cadenas a través de mi aplicación a través de temas seleccionables por el usuario?

No has dicho de dónde vienen esas cuerdas. ¿Son recursos de la cadena? ¿Entradas de base de datos? ¿Qué estás recuperando de un servicio Web? ¿Algo más?

Asumo por el momento que estos son recursos de cadena. Por definición, necesitará tener N copias de las cadenas, una por tema.

Dado que el género y el estado piratico no son cosas rastreadas por Android como posibles clasificadores de conjuntos de recursos, no puede tener esos recursos de cadena en diferentes conjuntos de recursos. Aunque pueden estar en archivos diferentes (por ejemplo, res/values/strings_theme1.xml ), los nombres de archivo no forman parte de los identificadores de recursos para cadenas. Por lo tanto, usted terminará teniendo que usar algún tipo de prefijo / sufijo para realizar un seguimiento de qué cadenas pertenecen con qué temas (por ejemplo, @string/btn_submit_theme1 ).

Si estas cadenas no están cambiando en tiempo de ejecución – es sólo lo que está en su recurso de diseño – se puede tomar una página de la biblioteca de Chris Jenkins Caligrafía . Él tiene su propia subclase de LayoutInflater , utilizado para sobrecargar algunos de los atributos XML estándar. En su caso, su enfoque está en android:fontFamily , donde apoya esa asignación a un archivo de fuentes en los activos.

En su caso, podría sobrecargar android:text . En el archivo de diseño, en lugar de apuntar a cualquiera de sus cadenas reales, podría ser el nombre base del recurso de cadena deseado, sin ningún identificador de tema (por ejemplo, si las cadenas reales son @string/btn_submit_theme1 y kin, usted Podría tener android:text="btn_submit" ). Su subclase LayoutInflater ese valor, agregaría el sufijo de nombre de tema, use getIdentifier() en sus Resources para buscar el ID de recurso de cadena actual y de allí obtendrá la cadena atada a su tema.

Una variación en esto sería poner el nombre base en android:tag lugar de android:text . android:text podría apuntar a uno de sus recursos de cadena real, para ayudar con el diseño GUI y tal. Tu LayoutInflater agarraría la etiqueta y usarla para derivar la cadena derecha en tiempo de ejecución.

Si va a reemplazar texto con otro texto extraído de recursos de cadenas basados ​​en temas, podría aislar la lógica get-the-string-given-the-base-name en un método de utilidad estática en algún lugar que pueda aplicar.

Si bien este derecho inicialmente requerirá un poco de trabajo, se escalará a una complejidad arbitraria, en términos del número de widgets y cadenas de interfaz de usuario afectados. Todavía tiene que recordar agregar valores para todos los temas para las nuevas cadenas que defina (puntos de bonificación para crear una comprobación personalizada de Lint o una tarea Gradle para validar esto).

Sí, se puede hacer, y aquí es cómo: primero tendrás que definir un atributo de tema, así:

 <attr name="myStringAttr" format="string|reference" /> 

Luego, en sus temas, agregue esta línea

 <item name="myStringAttr">Yarrrrr!</item> 

o

 <item name="myStringAttr">@string/yarrrrr</item> 

A continuación, puede utilizar este atributo en un archivo XML como tal (tenga en cuenta el ? lugar de @ ).

 <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="?attr/myStringAttr" /> 

O, de código, así:

 public CharSequence resolveMyStringAttr(Context context) { Theme theme = context.getTheme(); TypedValue value = new TypedValue(); if (!theme.resolveAttribute(R.attr.myStringAttr, value, true)) { return null; } return value.string; } 

Dado que un recurso es sólo un int en el corazón se puede almacenar una serie de ellos en tiempo de ejecución y sustituirlos en forma procesal como usted los utiliza.


 <?xml version="1.0" encoding="utf-8"?> <resources> <string name="OK_NORMAL">Okay</string> <string name="OK_PIRATE">Yaaarrrr!</string> <string name="OK_NINJA">Hooooaaa!</string> </resources> 

 public enum ThemeMode { PIRATE, NINJA, NORMAL; } 

 public class MyThemeStrings { public static int OK_PIRATE = R.string.OK_PIRATE; public static int OK_NINJA = R.string.OK_NINJA; public static int OK_NORMAL = R.string.OK_NORMAL; } 

 public setOkButtonText(ThemeMode themeMode) { // buttonOk is instantiated elsewhere switch (themeMode) { case PIRATE: buttonOk.setText(MyThemeStrings.OK_PIRATE); break; case NINJA: buttonOk.setText(MyThemeStrings.OK_NINJA); break; default: Log.e(TAG, "Unhandled ThemeMode: " + themeMode.name()); // no break here so it flows into the NORMAL base case as a default case NORMAL: buttonOk.setText(MyThemeStrings.OK_NORMAL); break; } } 

Aunque, habiendo escrito todo eso, probablemente hay una mejor manera de hacer todo esto a través de archivos XML separados. Voy a mirar en él ahora y escribir una segunda solución si encuentro uno.

Ok, tengo una segunda opción que en realidad puede ser más fácil de mantener y mantener su código más limpio, aunque puede ser más hambre de recursos debido a la carga de una matriz para cada cadena. No lo he comparado pero lo ofreceré como otra opción pero no lo usaría si ofreces demasiadas opciones de temas.


 public enum ThemeMode { NORMAL(0), PIRATE(1), NINJA(2); private int index; private ThemeMode(int index) { this.index = index; } public int getIndex() { return this.index; } } 

 <resources> <!-- ALWAYS define strings in the correct order according to the index values defined in the enum --> <string-array name="OK_ARRAY"> <item>OK</item> <item>Yaarrrr!</item> <item>Hooaaaa!</item> </string-array> <string-array name="CANCEL_ARRAY"> <item>Cancel</item> <item>Naarrrrr!</item> <item>Wha!</item> </string-array> </resources> 

 public setButtonTexts(Context context, ThemeMode themeMode) { // buttons are instantiated elsewhere buttonOk.setText(context.getResources() .getStringArray(R.array.CANCEL_ARRAY)[themeMode.getIndex()]); buttonCancel.setText(context.getResources() .getStringArray(R.array.OK_ARRAY)[themeMode.getIndex()]); } 

Por lo tanto, no he tenido la oportunidad de probar esto, pero de leer el archivo en Locale parece que puede crear su propia ubicación.

http://developer.android.com/reference/java/util/Locale.html

Y ayuda de otro stackoverflow

Establecer la configuración regional mediante programación

Un poco de combinación me lleva a:

 Locale pirate = new Locale("Pirate"); Configuration config = new Configuration(); config.locale = pirate; this.getActivity().getBaseContext().getResources() .updateConfiguration(config, this.getActivity().getBaseContext().getResources().getDisplayMetrics()); 

Creo que esto le permitiría tener res / values-pirate / strings como un recurso válido que se utilizaría cuando usted es un pirata. Cualquier cadena o configuración que no se anule volvería a res / values ​​/ … de forma predeterminada para que pudiera hacer esto para tantos temas como desee. Una vez más asumiendo que funciona.

  • ¿Cómo uso estilos de interfaz de usuario comunes en Android?
  • Utiliza el tema de Holo en la aplicación Android de Qt
  • Java.lang.RuntimeException Theme.Sherlock
  • Temas de Android ListView
  • Transición cuando el cambio de tema (o Activity.recreate ())
  • Temas aparentemente ignorados en ICS
  • Cambio de tema de aplicación a nivel de programación?
  • Lollipop estilo EditBox
  • Android ActionBar no cambia el color de fondo
  • ¿Cómo establecer el tema en ProgressDialog?
  • Barra de herramientas de AppCompat popupTheme no se utiliza en ShareAction MenuItem
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.