Strings.xml dosyasındaki başka bir dizeden bir dizeye başvuru?


230

Aşağıdaki gibi benim strings.xml dosyasında başka bir dizeden bir dizeye başvurmak istiyorum (özellikle "message_text" dize içeriğinin sonuna dikkat edin):

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

Yukarıdaki sözdizimi denedim ama sonra metin "@ string / button_text" düz metin olarak yazdırır. İstediğim gibi değil. İleti metninin "Henüz bir öğeniz yok! 'Öğe ekle' düğmesine basarak bir tane ekleyin."

İstediğimi başarmanın bilinen bir yolu var mı?

RATIONALE:
Uygulamamda bir öğe listesi var, ancak bu liste boş olduğunda bunun yerine "@android: id / empty" TextView gösteririm. Bu TextView içindeki metin kullanıcıya yeni bir öğenin nasıl ekleneceğini bildirmektir. Düzenimi değişikliklere karşı en iyi şekilde yapmak istiyorum (evet, söz konusu aptalım :-)


3
Başka bir benzer sorunun cevabı benim için çalıştı. Java gerekmez, ancak yalnızca aynı kaynak dosyasında çalışır.
mpkuth

Yanıtlar:


253

Java kodu kullanmadan sık kullanılan bir dizeyi (ör. Uygulama adı) xml'ye eklemenin güzel bir yolu: kaynak

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

GÜNCELLEME:

Varlığınızı küresel olarak tanımlayabilirsiniz, örneğin:

res / ham / entities.ent:

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

res / değerler / 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>

9
OP için doğru cevap budur. Bu çözümün başka büyük faydaları vardır: (1) DTD'yi harici bir dosyada tanımlayabilir ve ikame işlemini kaynaklarınızdaki her yere uygulamak için herhangi bir kaynak dosyasından referans alabilirsiniz. (2) android studio tanımlanmış varlıkları yeniden adlandırmak / referanslar / refactor sağlar. Yine de, yazarken adları otomatik olarak tamamlamaz. (
androidstudio

3
@RJFares İki yoldan gidebilirsiniz: (a) EG tüm varlık dosyası "dahil": bu cevaba bakın . VEYA (b) : burada gösterildiği gibi harici bir DTD'de tanımlanan her bir varlık dahil . İkisinin de mükemmel olmadığını unutmayın, çünkü (a) "yeniden düzenleme" kabinlerini kaybedeceksiniz ve (b) çok ayrıntılı
Mauro Panzeri

5
Bunu bir Android Kütüphane Projesi'nde kullanıyorsanız dikkatli olun - uygulamanızda üzerine yazamazsınız.
zyamys

4
dosyasında lezzetleri tanımlarken varlık değerini değiştirmek mümkün müdür?
Myoch

7
Harici varlıklar artık Android Studio tarafından desteklenmiyor, çünkü burada tartışılan bir hata var: stackoverflow.com/a/51330953/8154765
Davide Cannizzo

181

Dizenizin tamamı referans adından oluştuğu sürece bunlardan birine referans vermek mümkündür. Örneğin, bu işe yarayacaktır:

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

Varsayılan değerleri ayarlamak için daha da yararlıdır:

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

Artık string_defaultkodunuzun her yerini kullanabilirsiniz ve varsayılanı istediğiniz zaman kolayca değiştirebilirsiniz.


Ayrıca şu soruyu cevaplar: Bir etkinlik başlığındaki app_name dizesine nasıl başvurabilirim? :)
Stephen Hosking

53
Bu, "tüm dizeye başvurduğunuz sürece" (her zaman tanımlamaya göre) değil, "yönlendiren dize kaynağı yalnızca referans adından oluştuğu sürece" yazmalıdır.
sschuberth

54
Bu gerçekten bir referans değil, bu sadece dizeler oluşturma problemini çözmeyen bir takma addır.
Eric Woodruff

2
Bu soruya cevap vermiyor, bir dizenin diğerine gömülmesini istiyorlardı.
intrepidis

1
FWIW, bu benim için çok faydalı oldu. Teşekkür ederim.
Ellen Spertus

95

Bence yapamazsın. Ancak bir dizeyi istediğiniz gibi "biçimlendirebilirsiniz":

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

Kodda:

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

5
Aslında "mantıksız" bir çözüm umuyordum. Bununla birlikte, yeterince adil bir cevap (ve bunun hızı, size yeşil bir onay işareti verir :-)
dbm

34
String text = res.getString(R.string.message_text, res.getString(R.string.button_text));biraz daha temiz.
Andrey Novikov

34

Android'de xml içindeki Dizeleri birleştiremezsiniz

Aşağıdaki desteklenmez

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

Nasıl başaracağınızı öğrenmek için aşağıdaki bu bağlantıyı kontrol edin

Android XML'de birden fazla dizeyi nasıl birleştirebilirim?


16
Komik hikaye: bu referansta bulunduğunuz benzer bir soruya cevabım :-)
dbm

bunu arşivlemenin bir yolu var mı?

@Francesco Laurita cevabının bir kopyasıdır, programlı olarak bir dize nasıl değiştirilir.
CoolMind

14

Bir dizeyi diğerinden göndermenizi sağlayan basit gradle eklentisi oluşturdum . Başka bir dosyada tanımlanan dizelere başvurabilirsiniz, örneğin farklı yapı varyantı veya kitaplığı. Bu yaklaşımın eksileri - IDE refactor böyle referanslar bulamaz.

{{string_name}}Bir dizeye başvurmak için sözdizimini kullanın :

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

Eklentiyi entegre etmek için, uygulamanıza veya kütüphane modülü düzey build.gradledosyasına sonraki kodu eklemeniz yeterlidir

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"

GÜNCELLEME: Eklentinin yeni sürümü kaynakları .flat ikili biçiminde paketleyen aapt2 kullandığından kütüphane Android gradle eklentisi sürüm 3.0 ve üstü ile çalışmaz, bu nedenle paketlenmiş kaynaklar kütüphane için kullanılamaz. Geçici bir çözüm olarak gradle.properties dosyanızda android.enableAapt2 = false ayarını yaparak aapt2'yi devre dışı bırakabilirsiniz.


Bu fikri seviyorum .. ama bu hatayla benim için başarısız:> Could not get unknown property 'referencedString'
jpage4500

Bu aapt2 ile kesiliyor
Snicolas

2
Aynı şeyi yapan ve aapt2 ile çalışan bir eklenti oluşturdum: github.com/LikeTheSalad/android-string-reference :)
César Muñoz

7

İç içe dizeleri özyinelemeli olarak çözen kendi mantığınızı kullanabilirsiniz.

/**
 * 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());
}

Bir itibaren Activity, örneğin, o kadar gibi çağrı

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

2

Bunun daha eski bir yazı olduğunun farkındayım, ama benim bir proje için bulduğum hızlı 'n kirli çözümü paylaşmak istedim. Yalnızca TextViews için çalışır, ancak diğer widget'lara da uyarlanabilir. Bağlantının köşeli parantez içine alınması gerektiğini unutmayın (örn. [@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);
    }
}

Bu yaklaşımın dezavantajı olduğunu setText()dönüştürür CharSequencea Stringbir gibi şeyler geçerse bir konudur, SpannableString; projem için bu bir sorun değildi çünkü onu sadece TextViewsbenim bilgisayarımdan erişmem gerekmediği için kullandım Activity.


Bu soruya verilen cevaba en yakın şey budur. Sanırım düzen xml ayrıştırma içine kanca bakmak gerekir.
Eric Woodruff

2

Yeni veri bağlama ile birleştirebilir ve xml'nizde çok daha fazlasını yapabilirsiniz.

örneğin mesaj1 ve mesaj2'niz varsa şunları yapabilirsiniz:

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

hatta bazı metin araçlarını içe aktarabilir ve String.format ve arkadaşlarını çağırabilirsiniz.

ne yazık ki dağınık olabilir birkaç yerde yeniden kullanmak istiyorsanız, bu kod parçaları her yerde istemiyorum. ve bunları tek bir yerde (bildiğim kadarıyla) xml'de tanımlayamazsınız, böylece bu kompozisyonları kapsülleyecek bir sınıf oluşturabilirsiniz:

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

bunun yerine kullanabilirsiniz (sınıfı veri bağlama ile içe aktarmanız gerekir)

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

2

Francesco Laurita'nın yukarıdaki cevabına ek olarak https://stackoverflow.com/a/39870268/9400836

Bir derleme hatası var gibi görünüyor "& varlık; başvuruda bulunuldu, ancak bildirilmedi" gibi harici bildirime başvurarak çözülebilir

res / ham / entities.ent

<!ENTITY appname "My App Name">

res / değerler / 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

Derleyip çalıştırmasına rağmen boş bir değere sahiptir. Belki birisi bunu nasıl çözeceğini bilir. Ben bir yorum gönderirdi ama minimum itibar 50.


1
Şimdi
53'ünüz

1

Dize yer tutucularını (% s) kullanabilir ve çalışma zamanında java kullanarak değiştirebilirsiniz

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

ve java'da

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

ve ardından dizeyi kullandığı yere ayarlayın


Diğer çözümleri kopyalarsınız. Birkaç dizeleri, kullanımını başvurduğunuzda %1$s, %2$svb yerine %s.
CoolMind

@CoolMind kopyanın referansından bahsedebilir misiniz?
Ismail Iqbal

1

Oluşturma sırasında bu yer tutucuları çözmenizi sağlayan küçük bir kütüphane oluşturdum, böylece istediğinizi elde etmek için herhangi bir Java / Kotlin kodu eklemenize gerek kalmayacak.

Örneğinize göre, dizelerinizi şu şekilde ayarlamanız gerekir:

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

Ve sonra eklenti aşağıdakileri oluşturmaya özen gösterecektir:

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

Yerelleştirilmiş ve lezzet dizeleri için de çalışır, ayrıca şablonlarınızda veya değerlerinde değişiklik yaptığınızda oluşturulan dizeler kendilerini güncel tutacaktır.

Daha fazla bilgi burada: https://github.com/LikeTheSalad/android-string-reference


Kütüphanenizi denedim. Verdiğiniz adımları uyguladıktan sonra. Yapı hataları vermeye devam ediyor. Kütüphaneniz hiç çalışmıyor
developer1011

Anladım, duyduğuma üzüldüm. İsterseniz, lütfen burada bir sorun oluşturun: github.com/LikeTheSalad/android-string-reference/issues günlükleri ile ve bir özellik sorunu olması durumunda en kısa sürede ele alabilirim veya bir yapılandırma sorunu varsa da oradasın. 👍
César Muñoz
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.