¿Hace referencia a una cadena de otra cadena en strings.xml?

Me gustaría hacer referencia a una cadena de otra cadena en mi archivo strings.xml, como a continuación (específicamente observe el final del contenido de la cadena “message_text”):

  Add item You don't have any items yet! Add one by pressing the \'@string/button_text\' button.  

Probé la syntax anterior, pero luego el texto imprime “@ string / button_text” como texto claro. No es lo que quiero Me gustaría que se imprima el texto del mensaje “¡Aún no tiene ningún elemento! Agregue uno presionando el botón ‘Agregar elemento'”.

¿Hay alguna manera conocida de lograr lo que quiero?

RAZÓN FUNDAMENTAL:
Mi aplicación tiene una lista de elementos, pero cuando esa lista está vacía, muestro una “@android: id / empty” TextView en su lugar. El texto en ese TextView es para informar al usuario cómo agregar un nuevo elemento. Me gustaría hacer mi diseño a prueba de cambios (sí, soy el tonto en cuestión 🙂

Una buena manera de insertar una cadena de uso frecuente (por ejemplo, el nombre de la aplicación) en xml sin usar código Java: fuente

  < ?xml version="1.0" encoding="utf-8"?> < !DOCTYPE resources [  < !ENTITY author "MrGreen"> ]>  &appname; The &appname; app was created by &author;  

ACTUALIZAR:

Incluso puede definir su entidad globaly por ejemplo:

res / raw / entities.ent:

 < !ENTITY appname "MyAppName"> < !ENTITY author "MrGreen"> 

res / values ​​/ string.xml:

 < ?xml version="1.0" encoding="utf-8"?> < !DOCTYPE resources [  %ents; ]>  &appname; The &appname; app was created by &author;  

Es posible hacer referencia a uno dentro de otro siempre que toda la cadena consista en el nombre de referencia. Por ejemplo, esto funcionará:

 My App @string/app_name @string/app_name 

Es aún más útil para establecer valores predeterminados:

 String 1 String 2 String 3 @string/string1 

Ahora puede usar string_default en todas partes en su código y puede cambiar fácilmente el valor predeterminado en cualquier momento.

Creo que no puedes. Pero puede “formatear” una cadena como desee:

 < ?xml version="1.0" encoding="utf-8"?>  Add item You don't have any items yet! Add one by pressing the %1$s button.  

En el código:

 Resources res = getResources(); String text = String.format(res.getString(R.string.message_text), res.getString(R.string.button_text)); 

En Android no puedes concatenar cadenas dentro de xml

Lo siguiente no es compatible

 @string/string1 TEST 

Consulte este enlace a continuación para saber cómo lograrlo

Cómo concatenar múltiples cadenas en Android XML?

Creé un plugin de gradle simple que le permite referir una cadena de otra. Puede referir cadenas que están definidas en otro archivo, por ejemplo, en una variante de comstackción o biblioteca diferente. En contra de este enfoque, IDE refactor no encontrará tales referencias.

Use la syntax de {{string_name}} para referir una cadena:

 < ?xml version="1.0" encoding="utf-8"?>  Super My {{super}} App Name of my application is: {{app_name}}  

Para integrar el complemento, simplemente agregue el siguiente código en su aplicación o archivo de nivel de biblioteca build.gradle

 buildscript { repositories { maven { url "https://plugins.gradle.org/m2/" } } dependencies { classpath "gradle.plugin.android-text-resolver:buildSrc:1.2.0" } } apply plugin: "com.icesmith.androidtextresolver" 

ACTUALIZACIÓN: La biblioteca no funciona con Android Gradle Plugin versión 3.0 y superior porque la nueva versión del complemento utiliza aapt2, que empaqueta recursos en formato binario .flat, por lo que los recursos empaquetados no están disponibles para la biblioteca. Como solución temporal, puede desactivar aapt2 configurando android.enableAapt2 = false en su archivo gradle.properties.

Puede usar su propia lógica, que resuelve las cadenas anidadas recursivamente.

 /** * Regex that matches a resource string such as @string/a-b_c1. */ private static final String REGEX_RESOURCE_STRING = "@string/([A-Za-z0-9-_]*)"; /** Name of the resource type "string" as in @string/... */ private static final String DEF_TYPE_STRING = "string"; /** * Recursively replaces resources such as @string/abc with * their localized values from the app's resource strings (eg * strings.xml) within a source string. * * Also works recursively, that is, when a resource contains another * resource that contains another resource, etc. * * @param source * @return source with replaced resources (if they exist) */ public static String replaceResourceStrings(Context context, String source) { // Recursively resolve strings Pattern p = Pattern.compile(REGEX_RESOURCE_STRING); Matcher m = p.matcher(source); StringBuffer sb = new StringBuffer(); while (m.find()) { String stringFromResources = getStringByName(context, m.group(1)); if (stringFromResources == null) { Log.w(Constants.LOG, "No String resource found for ID \"" + m.group(1) + "\" while inserting resources"); /* * No need to try to load from defaults, android is trying that * for us. If we're here, the resource does not exist. Just * return its ID. */ stringFromResources = m.group(1); } m.appendReplacement(sb, // Recurse replaceResourceStrings(context, stringFromResources)); } m.appendTail(sb); return sb.toString(); } /** * Returns the string value of a string resource (eg defined in * values.xml). * * @param name * @return the value of the string resource or null if no * resource found for id */ public static String getStringByName(Context context, String name) { int resourceId = getResourceId(context, DEF_TYPE_STRING, name); if (resourceId != 0) { return context.getString(resourceId); } else { return null; } } /** * Finds the numeric id of a string resource (eg defined in * values.xml). * * @param defType * Optional default resource type to find, if "type/" is not * included in the name. Can be null to require an explicit type. * * @param name * the name of the desired resource * @return the associated resource identifier. Returns 0 if no such resource * was found. (0 is not a valid resource ID.) */ private static int getResourceId(Context context, String defType, String name) { return context.getResources().getIdentifier(name, defType, context.getPackageName()); } 

De una Activity , por ejemplo, llámalo así

 replaceResourceStrings(this, getString(R.string.message_text)); 

Soy consciente de que esta es una publicación más antigua, pero quería compartir la solución rápida y sucia que he creado para un proyecto mío. Solo funciona para TextViews, pero también se puede adaptar a otros widgets. Tenga en cuenta que requiere que el enlace se incluya entre corchetes (por ejemplo, [@string/foo] ).

 public class RefResolvingTextView extends TextView { // ... @Override public void setText(CharSequence text, BufferType type) { final StringBuilder sb = new StringBuilder(text); final String defPackage = getContext().getApplicationContext(). getPackageName(); int beg; while((beg = sb.indexOf("[@string/")) != -1) { int end = sb.indexOf("]", beg); String name = sb.substring(beg + 2, end); int resId = getResources().getIdentifier(name, null, defPackage); if(resId == 0) { throw new IllegalArgumentException( "Failed to resolve link to @" + name); } sb.replace(beg, end + 1, getContext().getString(resId)); } super.setText(sb, type); } } 

La desventaja de este enfoque es que setText() convierte CharSequence en una String , lo cual es un problema si pasas cosas como SpannableString ; para mi proyecto esto no fue un problema ya que solo lo TextViews para TextViews que no tuve que acceder desde mi Activity .

Con el nuevo enlace de datos puede concatenar y hacer mucho más en su xml.

por ejemplo, si tienes message1 y message2 puedes:

 android:text="@{@string/message1 + ': ' + @string/message2}" 

incluso puede importar algunas utilidades de texto y llamar a String.format y sus amigos.

Desafortunadamente, si quiere reutilizarlo en varios lugares puede ser complicado, no quiere que este código se rompa en todas partes. y no puedes definirlos en xml en un solo lugar (no que yo sepa) así que para eso puedes crear una clase que encapsule esas composiciones:

 public final class StringCompositions { public static final String completeMessage = getString(R.string.message1) + ": " + getString(R.string.message2); } 

luego puede usarlo en su lugar (deberá importar la clase con enlace de datos)

 android:text="@{StringCompositions.completeMessage}" 

Además de la respuesta anterior de Francesco Laurita https://stackoverflow.com/a/39870268/9400836

Parece que hay un error de comstackción “& entity; se hizo referencia, pero no se declaró”, que se puede resolver haciendo referencia a la statement externa como esta

res / raw / entities.ent

 < !ENTITY appname "My App Name"> 

res / values ​​/ strings.xml

 < ?xml version="1.0" encoding="utf-8"?> < !DOCTYPE resources [  ]>  &appname;  

Aunque comstack y ejecuta, tiene un valor vacío. Quizás alguien sepa cómo resolver esto. Hubiera publicado un comentario pero la reputación mínima es 50.

Puede usar marcadores de posición de cadena (% s) y reemplazarlos usando java en tiempo de ejecución

  Add item Custom text %s   

y en java

 String final = String.format(getString(R.string.message_text),getString(R.string.button_text)); 

y luego establecerlo en el lugar donde usa la cadena