theme textappearance temas style studio para estilos create crear apptheme android string resources themes customization

textappearance - theme editor android studio



Tema dependiente de cadenas de Android (5)

Digamos que tengo una aplicación con 2 temas: masculino y femenino. Los temas simplemente cambian la paleta de colores y algunos elementos dibujables para atraer los gustos preferidos del usuario.

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

Pero ahora digamos que quiero ser un poco más lindo con la aplicación y no solo cambiar los colores y los elementos dibujables, sino también cambiar las cuerdas. Por ejemplo, podría querer agregar un tema pirata y luego "Enviar" sería "Arrrrgh!"

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

Editar:

Hacer esto: la aplicación tiene 12 botones y 32 vistas de texto Me gustaría que el tema dependa y me gustaría lograr esto sin un mapeo gigante o una serie de atributos personalizados.

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


Digamos que tengo una aplicación con 2 temas: masculino y femenino. Los temas simplemente cambian la paleta de colores y algunos elementos dibujables para atraer los gustos preferidos del usuario.

¿Qué tal si pretendemos que estás haciendo otra cosa? Este es un diseño anti patrón, asociando colores particulares basados ​​en el género (p. Ej., "Chicas como rosa").

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

Por ejemplo, podría querer agregar un tema pirata y luego "Enviar" sería "Arrrrgh!"

¡Solo si "Cancelar" se asigna a "Avast!"

¿Cómo puedo cambiar las cadenas 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 cadena? Entradas de la base de datos ¿A los que está recuperando de un servicio web? ¿Algo más?

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

Dado que el género y el estado pirata no son elementos que Android rastrea como posibles calificadores del conjunto de recursos, no puede tener esos recursos de cadena en diferentes conjuntos de recursos. Si bien podrían estar en diferentes archivos (por ejemplo, res/values/strings_theme1.xml ), los nombres de archivo no son parte de los identificadores de recursos para las cadenas. Por lo tanto, terminará teniendo que usar algún tipo de prefijo / sufijo para realizar un seguimiento de qué cadenas pertenecen a qué temas (por ejemplo, @string/btn_submit_theme1 ).

Si estas cadenas no cambian en tiempo de ejecución, es solo lo que está en su recurso de diseño, podría tomar una página de la biblioteca de Caligrafía de Chris Jenkins . Él tiene su propia subclase de LayoutInflater , que se usa para sobrecargar algunos de los atributos XML estándar. En su caso, su atención se centra en android:fontFamily , donde admite esa asignación a un archivo de fuente en los activos.

En tu caso, podrías sobrecargar android:text . En su archivo de diseño, en lugar de apuntar a cualquiera de sus cadenas reales, podría hacer que sea el nombre base de su 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 tomaría ese valor, agregaría el sufijo del nombre del tema, usar getIdentifier() en sus Resources para buscar el ID del recurso de cadena real, y de allí obtener la cadena atada a su tema.

Una variación de 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 reales, para ayudar con el diseño de la GUI y demás. Su LayoutInflater agarraría la etiqueta y la usaría para derivar la cadena correcta en el tiempo de ejecución.

Si va a reemplazar texto con otro texto extraído de recursos de cadena basados ​​en temas, puede aislar su lógica de obtener la cadena dada por el nombre base en un método de utilidad estática en algún lugar que pueda aplicar.

Si bien al principio hacer esto bien tomará un poco de trabajo, se escalará a una complejidad arbitraria, en términos del número de widgets y cadenas de UI afectados. Aún debe recordar agregar valores para todos los temas para cualquier cadena nueva que defina (puntos de bonificación por crear una comprobación de pelusa personalizada o una tarea de Gradle para validar esto).


Como un recurso es solo un int en el fondo, puede almacenar varios de ellos en tiempo de ejecución y sustituirlos en el procedimiento a medida que 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 haya una mejor manera de hacer todo esto a través de archivos XML separados. Lo investigaré ahora y escribiré una segunda solución si encuentro una.


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 que tenga más 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 ofrece demasiadas opciones de tema.

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 al 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

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 te permitiría tener res / values-pirate / strings como un recurso válido real que se usaría cuando seas pirata. Cualquier cadena o configuración que no anule volvería entonces a res / values ​​/ ... de manera predeterminada, de modo que podría hacer esto para tantos temas como desee. Otra vez suponiendo que funciona.


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

<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 usar este atributo en un archivo XML como ese (tenga en cuenta el ? lugar de @ ).

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

o, del 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; }