Google
 

Friday, September 2, 2011

Android Resources: Map Structures

Resource files host a very limited set of data types for Android developers. In a recent project, I wanted to have a mapped data set (in Java, the data type would have been Map<String, Map<String, Integer>>). I looked around the internet trying to find out how to do it, and the resounding answer was that it's not possible. Options include creating a <string-array> and an <integer-array>, and merging them into a Map at runtime, or alternatively creating a series of <string> resources, and merging them into a Map using reflection.

In my opinion, neither is a particularly good option for creating a resource, when the supported types work so seamlessly. So, I spent time developing a workaround. It turns our that the <array> type is functionally similar to Object[]. Each <item> can be any simple type (int, string, etc.), or else a reference to an existing resource (of any type). This means you can in fact have a nested array, you simply have to write each array separately and then use a reference in one to the other.

<resources> <array name="my_array"> <item>@array/a0</item> <item>@array/a1</item> <item>@array/a2</item> </array> <array name="a0"> <item>0,0</item> <item>0,1</item> <item>0,2</item> </array> <array name="a1"> <item>1,0</item> <item>1,1</item> <item>1,2</item> </array> <array name="a2"> <item>2,0</item> <item>2,1</item> <item>2,2</item> </array> </resources>

Unfortunately, you can't simply load the resource as a Map or Object[][]. It's a little more complicated than that. You've got to load the TypedArray ('my_array'), loop through the values, obtain the resourceId for each of those values, use the resourceId to load the sub-array as another TypedArray, and then loop through those values. Further levels adds more steps of the same actions.

TypedArray myArray = getResources().obtainTypedArray(R.array.my_array); int columns = myArray.length(); for(int i = 0; i < columns; i++) { int rowid = myArray.peekValue(i).resourceId; TypedArray row = getResources().obtainTypedArray(rowid); int values = row.length(); for(int k = 0; k < values; k++) { System.out.print(row.peekValue(i).coerceToString() + "\t"); } System.out.println(); }

I have not tried creating array which reference each other, but it's definitely a bad idea. I suspect it'll result in a compile-time error, but I don't intend to try.

No comments: