287

I have preferences where you can enable/disable what items will show up on the menu. There are 17 items. I made a string array in values/arrays.xml with titles for each of these 17 items.

I have preferences.xml which has the layout for my preferences file, and I would like to reference a single item from the string array to use as the title.

How can I do this?

In the Android developer reference, I see how I can reference a single string with XML, but not how I can reference a string from an array resource in XML.

Jonathan Soifer
  • 2,715
  • 6
  • 27
  • 50
Jorsher
  • 3,683
  • 3
  • 17
  • 14

6 Answers6

371

In short: I don't think you can, but there seems to be a workaround:.

If you take a look into the Android Resource here:

http://developer.android.com/guide/topics/resources/string-resource.html

You see than under the array section (string array, at least), the "RESOURCE REFERENCE" (as you get from an XML) does not specify a way to address the individual items. You can even try in your XML to use "@array/yourarrayhere". I know that in design time you will get the first item. But that is of no practical use if you want to use, let's say... the second, of course.

HOWEVER, there is a trick you can do. See here:

Referencing an XML string in an XML Array (Android)

You can "cheat" (not really) the array definition by addressing independent strings INSIDE the definition of the array. For example, in your strings.xml:

<string name="earth">Earth</string>
<string name="moon">Moon</string>

<string-array name="system">
    <item>@string/earth</item>
    <item>@string/moon</item>
</string-array>

By using this, you can use "@string/earth" and "@string/moon" normally in your "android:text" and "android:title" XML fields, and yet you won't lose the ability to use the array definition for whatever purposes you intended in the first place.

Seems to work here on my Eclipse. Why don't you try and tell us if it works? :-)

Community
  • 1
  • 1
davidcesarino
  • 16,160
  • 16
  • 68
  • 109
  • Thanks, this is what I did ;) I was in the middle of typing my answer when you posted yours. Thanks! – Jorsher Nov 12 '10 at 04:40
  • Can you please accept the answer by clicking on the green tick? :-P – davidcesarino Nov 12 '10 at 04:43
  • And by the way, you're welcome. I think, too, that Android should have a way to address an array in XML without resorting to that verbose workaround. Something like "@array/yourarray(1)" But at least it works. – davidcesarino Nov 12 '10 at 04:59
  • 35
    if we could only give `name` tags to s...*sigh* – Some Noob Student Feb 28 '11 at 07:58
  • What happens if we define the array in the default `/values/strings.xml` then you want to use a string from `/values-fr/strings.xml` that file does not contain the array, sucks! – Blundell Mar 07 '12 at 22:54
  • 2
    Just tried it! Defined the array in the default `strings.xml` but didn't in `values-fr` and the array still updated with the french version. So wrong .. but cool it works! – Blundell Mar 07 '12 at 22:57
  • It makes sense when you think about it: 1) the array is a declaration in itself, distinct from the strings it uses (in the method posted in my answer); and 2) the `values` is applied subsidiarily to other locales. Therefore, whenever you declare the array only in `values`, using this method, you only need to translate the strings, since the array declaration will be applied subsidiarily to the french locale. However, the strings inside the array are referenced to the french locale because this already has those strings. It really doesn't surprise me when you have the documentation in mind. – davidcesarino Mar 07 '12 at 23:48
  • But, of course... thanks for letting me/us know... I didn't think about all this until now! For example: now I realize that since you don't need to translate the entire array every time, it's easier to offload the work to someone else (a translator) that doesn't understand xml structure very well (and could mess things). – davidcesarino Mar 07 '12 at 23:52
  • I would not call it a workaround, rather a better way of achieving the same result. As for verbosity, in my opinion it makes things clearer. – comodoro Sep 15 '12 at 16:25
  • More work up front but in the end there should be less work to maintain it... hopefully... thanks for this workaround! – ArtOfWarfare Oct 22 '12 at 17:38
  • 1
    Quick tip to anyone: Double clicking on "earth" will highlight earth. Double clicking on the moon in @string/moon will highlight just moon. Thus, if you duplicate the a first item several times when building your array, you can then copy and paste between your individual strings and the string-array fairly easily. Might help with speeding up the creation of your string-array :) – ArtOfWarfare Oct 22 '12 at 17:46
  • 2
    Just want to know if this approach cause worse performance than if I declare strings in array as they are but not using references? – Alex Bonel Jul 26 '13 at 11:52
  • 1
    @AlexBonel an interesting question, and I honestly don't know. Even if it would, I think we should leave this kind of very micro optimization to the compiler. Anyway, don't they end inlined somewhere down the road? I'd guess they do, but I'm not sure. At least on a higher level in the Android chain, I know you can fully disassemble an APK with apktool, and it will recover the original names of the string keys, which means that referenced declarations, like the one above, are kept. But I'm not very interested in that area right now, so I can't answer your question. It's a good question though. – davidcesarino Jul 26 '13 at 18:14
  • Any way thank you for the respond. You mentioned that if we decompile an APK then we would see original names of references that means that they are processed only at running time and so XmlParser needs a time to find those strings which they refer to in xml (or maybe even in other). I'm just not sure that this is a kind of micro optimization, but I may be wrong. – Alex Bonel Jul 30 '13 at 07:43
  • Yes, you can recover the original logical names, because they are kept inside the binary `resources.arsc` file, inside the APK. That file is, AFAIK, a *lookup table*, so I really don't think we can assume that the resource compilation won't just inline the address reference to the original content. Again, I don't know the structure of the `resources.arsc` file. However, even if that means looking at two address locations instead of one, directly (and I doubt it), would that really be wasteful to a point we should worry about it and compromise maintenance? That was my original point. – davidcesarino Jul 30 '13 at 18:07
  • Why don't you ask that? I'd love to see a proper answer. The only reason I didn't ask is because you thought about that, so I don't want to pretend like I did. That is, unless you don't mind, then I'm going to ask right away. :) – davidcesarino Jul 30 '13 at 18:10
250

Maybe this would help:

String[] some_array = getResources().getStringArray(R.array.your_string_array)

So you get the array-list as a String[] and then choose any i, some_array[i].

  • 12
    I understand how to get the array resource. That doesn't allow me to reference the item from the array in an xml layout though. – Jorsher Nov 12 '10 at 03:31
  • 1
    Ah, sorry. I misunderstood. Well, does it have to be in the xml layout? If the title changes with user choice, why not just do it in the .java file. E.g. .setText(some_array[i]). –  Nov 12 '10 at 03:39
  • 3
    Because, the preferences layout is in xml. The java loads the menu with the items that are "enabled" in preferences. Typing up 17 checks for preferences, and the subsequent code to add it to the listview just seems redundant and sloppy to me. – Jorsher Nov 12 '10 at 04:22
  • 1
    This was useful when I was creating an arrayadapter from an xml array in my values folder – EHarpham Jan 02 '13 at 17:36
  • I found this useful when needing a default value when getting a preference: `String myPref = preferences.getString(context.getResources().getString(R.string.my_pref), some_array [0]);` where my_pref was previously set to some value some_array[x] – Al Lelopath Oct 06 '16 at 14:49
75

The better option would be to just use the resource returned array as an array, meaning:

getResources().getStringArray(R.array.your_array)[position]

This is a shortcut approach of other mentioned approaches but does the work in the fashion you want. Otherwise Android doesn't provide direct XML indexing for XML based arrays.

Null
  • 1,950
  • 9
  • 30
  • 33
Amitesh
  • 992
  • 7
  • 11
11

Unfortunately:

  • It seems you can not reference a single item from an array in values/arrays.xml with XML. Of course you can in Java, but not XML. There's no information on doing so in the Android developer reference, and I could not find any anywhere else.

  • It seems you can't use an array as a key in the preferences layout. Each key has to be a single value with it's own key name.

What I want to accomplish: I want to be able to loop through the 17 preferences, check if the item is checked, and if it is, load the string from the string array for that preference name.

Here's the code I was hoping would complete this task:

SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());  
ArrayAdapter<String> itemsArrayList = new ArrayAdapter<String>(getBaseContext(),   android.R.layout.simple_list_item_1);  
String[] itemNames = getResources().getStringArray(R.array.itemNames_array);  


for (int i = 0; i < 16; i++) {  
    if (prefs.getBoolean("itemKey[i]", true)) {  
        itemsArrayList.add(itemNames[i]);  
    }  
} 

What I did:

  • I set a single string for each of the items, and referenced the single strings in the . I use the single string reference for the preferences layout checkbox titles, and the array for my loop.

  • To loop through the preferences, I just named the keys like key1, key2, key3, etc. Since you reference a key with a string, you have the option to "build" the key name at runtime.

Here's the new code:

for (int i = 0; i < 16; i++) {  
        if (prefs.getBoolean("itemKey" + String.valueOf(i), true)) {  
        itemsArrayList.add(itemNames[i]);  
    }  
}
Jorsher
  • 3,683
  • 3
  • 17
  • 14
3

Another way of doing it is defining a resources array in strings.xml like below.

<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE resources [
    <!ENTITY supportDefaultSelection "Choose your issue">
    <!ENTITY issueOption1 "Support">
    <!ENTITY issueOption2 "Feedback">
    <!ENTITY issueOption3 "Help">
    ]>

and then defining a string array using the above resources

<string-array name="support_issues_array">
        <item>&supportDefaultSelection;</item>
        <item>&issueOption1;</item>
        <item>&issueOption2;</item>
        <item>&issueOption3;</item>
    </string-array>

You could refer the same string into other xmls too keeping DRY intact. The advantage I see is, with a single value change it would effect all the references in the code.

Hanu
  • 1,087
  • 11
  • 21
  • Worth mentioning that this solution works ONLY within one file, strings.xml in this case. If you want to use an item in a layout file, it won't be possible. – Rowan Jun 13 '22 at 07:26
0

The answer is quite easy to implement.

String[] arrayName = getResources().getStringArray(R.array.your_string_array);

and now you can access any element of the array by index (let suppose i'th index), then you can access it by arrayName[i]

I hope you understand this

  • for static content of string array you can not changes in xml, but you can changes via get it from string[] and after you can change it . – Gaurav Mandlik Aug 08 '22 at 12:40
  • This doesn't work. People coming here are looking for a way to access items by name, not by array position int. – edgar_wideman Dec 24 '22 at 15:36