Programing

strings.xml의 다른 문자열에서 하나의 문자열을 참조 하시겠습니까?

lottogame 2020. 4. 27. 07:57
반응형

strings.xml의 다른 문자열에서 하나의 문자열을 참조 하시겠습니까?


아래와 같이 strings.xml 파일에서 다른 문자열의 문자열을 참조하고 싶습니다 (특히 "message_text"문자열 내용의 끝 부분에 유의하십시오).

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="button_text">Add item</string>
    <string name="message_text">You don't have any items yet! Add one by pressing the \'@string/button_text\' button.</string>
</resources>

위의 구문을 시도했지만 텍스트가 "@ string / button_text"를 일반 텍스트로 인쇄합니다. 내가 원하는 것이 아닙니다. "아직 항목이 없습니다! '항목 추가'버튼을 눌러 추가하십시오."라는 메시지 텍스트를 인쇄하고 싶습니다.

내가 원하는 것을 달성하는 알려진 방법이 있습니까?

RATIONALE :
응용 프로그램에 항목 목록이 있지만 해당 목록이 비어 있으면 대신 "@android : id / empty"TextView가 표시됩니다. 해당 TextView의 텍스트는 사용자에게 새 항목을 추가하는 방법을 알려주는 것입니다. 내 레이아웃을 변경에 바보처럼 만들고 싶습니다 (예, 나는 바보입니다 :-)


Java 코드를 사용하지 않고 자주 사용하는 문자열 (예 : 앱 이름)을 XML에 삽입하는 좋은 방법 : source

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE resources [
      <!ENTITY appname "MyAppName">
      <!ENTITY author "MrGreen">
    ]>

<resources>
    <string name="app_name">&appname;</string>
    <string name="description">The &appname; app was created by &author;</string>
</resources>

최신 정보:

다음과 같이 엔터티를 전역 적으로 정의 할 수도 있습니다.

res / raw / entities.ent :

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

res / values ​​/ string.xml :

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE resources [
    <!ENTITY % ents SYSTEM "./res/raw/entities.ent">
    %ents;   
]>

<resources>
    <string name="app_name">&appname;</string>
    <string name="description">The &appname; app was created by &author;</string>
</resources>

전체 문자열이 참조 이름으로 구성되어있는 한 다른 참조를 참조 할 수 있습니다. 예를 들어 다음과 같이 작동합니다.

<string name="app_name">My App</string>
<string name="activity_title">@string/app_name</string>
<string name="message_title">@string/app_name</string>

기본값을 설정하는 데 훨씬 유용합니다.

<string name="string1">String 1</string>
<string name="string2">String 2</string>
<string name="string3">String 3</string>
<string name="string_default">@string/string1</string>

이제 string_default코드의 어느 곳에서나 사용할 수 있으며 언제든지 기본값을 쉽게 변경할 수 있습니다.


당신이 할 수 없다고 생각합니다. 그러나 원하는대로 문자열을 "포맷"할 수 있습니다.

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

코드에서 :

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

안드로이드에서는 XML 내부에서 문자열을 연결할 수 없습니다

다음은 지원되지 않습니다

<string name="string_default">@string/string1 TEST</string>

그것을 달성하는 방법을 알고 아래 링크를 확인하십시오

안드로이드 XML에서 여러 문자열을 연결하는 방법은 무엇입니까?


하나의 문자열을 다른 문자열에서 참조 할 수있는 간단한 gradle 플러그인만들었습니다 . 다른 빌드 변형 또는 라이브러리와 같은 다른 파일에 정의 된 문자열을 참조 할 수 있습니다. 이 접근법의 단점-IDE 리 팩터는 그러한 참조를 찾지 않습니다.

{{string_name}}문자열을 참조 하려면 구문을 사용 하십시오.

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

플러그인을 통합하려면 앱 또는 라이브러리 모듈 레벨 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"

업데이트 : 새로운 버전의 플러그인은 aapt2를 사용하여 리소스를 .flat 바이너리 형식으로 압축하므로 라이브러리에서 압축 된 리소스를 사용할 수 없으므로 라이브러리가 Android gradle 플러그인 버전 3.0 이상에서 작동하지 않습니다. 임시 솔루션으로 gradle.properties 파일에서 android.enableAapt2 = false를 설정하여 aapt2를 비활성화 할 수 있습니다.


You could use your own logic, that resolves the nested strings recursively.

/**
 * Regex that matches a resource string such as <code>@string/a-b_c1</code>.
 */
private static final String REGEX_RESOURCE_STRING = "@string/([A-Za-z0-9-_]*)";

/** Name of the resource type "string" as in <code>@string/...</code> */
private static final String DEF_TYPE_STRING = "string";

/**
 * Recursively replaces resources such as <code>@string/abc</code> with
 * their localized values from the app's resource strings (e.g.
 * <code>strings.xml</code>) within a <code>source</code> string.
 * 
 * Also works recursively, that is, when a resource contains another
 * resource that contains another resource, etc.
 * 
 * @param source
 * @return <code>source</code> 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 (e.g. defined in
 * <code>values.xml</code>).
 * 
 * @param name
 * @return the value of the string resource or <code>null</code> 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 (e.g. defined in
 * <code>values.xml</code>).
 * 
 * @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());
}

From an Activity, for example, call it like so

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

I'm aware that this is an older post, but I wanted to share the quick 'n dirty solution that I've come up with for a project of mine. It only works for TextViews but could be adapted to other widgets as well. Note that it requires the link to be enclosed in square brackets (e.g. [@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);
    }
}

The downside of this approach is that setText() converts the CharSequence to a String, which is an issue if you pass things like a SpannableString; for my project this wasn't an issue since I only used it for TextViews that I didn't need to access from my Activity.


In addition to the above answer by Francesco Laurita https://stackoverflow.com/a/39870268/9400836

It seems there is a compile error "&entity; was referenced, but not declared" which can be solved by referencing the external declaration like this

res/raw/entities.ent

<!ENTITY appname "My App Name">

res/values/strings.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE resources [
    <!ENTITY appname SYSTEM "/raw/entities.ent">
]>
<resources>
    <string name="app_name">&appname;</string>
</resources

Although it compiles and runs it has an empty value. Maybe someone knows how to solve this. I would have posted a comment but minimum reputation is 50.


You could use string placeholders (%s) and replace them using java at run-time

<resources>
<string name="button_text">Add item</string>
<string name="message_text">Custom text %s </string>
</resources>

and in java

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

and then set it to the place where it uses the string


With the new data binding you can concatenate and do much more in your xml.

for example if you got message1 and message2 you can:

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

you can even import some text utils and call String.format and friends.

unfortunately if you want to reuse it in several places it can get messy, you don't want this code pieces everywhere. and you can't define them in xml in one place (not that I know of) so for that you can create a class that will encapsulate those compositions:

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

then you can use it instead (you will need to import the class with data binding)

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

참고URL : https://stackoverflow.com/questions/4746058/reference-one-string-from-another-string-in-strings-xml

반응형