开发者

Android Resource - Array of Arrays

开发者 https://www.devze.com 2023-01-28 00:56 出处:网络
I am trying to implement a resource data structure that includes an array of arrays, specifically strings.The issue I run into is how to get the sub-array objects and their specific values.Here is wha

I am trying to implement a resource data structure that includes an array of arrays, specifically strings. The issue I run into is how to get the sub-array objects and their specific values. Here is what my resource file looks like....

<resources>
   <array name="array0">
     <item>
       <string-array name="array01">
         <item name="id">1</item>
         <item name="title">item one</item>
       </string-array>
     </item>
     <item>
       &开发者_StackOverflow中文版lt;string-array name="array02">
         <item name="id">2</item>
         <item name="title">item two</item>
       </string-array>
     </item>
     <item>
       <string-array name="array03">
         <item name="id">3</item>
         <item name="title">item three</item>
       </string-array>
     </item>
   </array>
</resources>

Then, in my Java code I retrieve the array and try to access the sub elements like so...

TypedArray typedArray = getResources().obtainTypedArray(R.array.array0);

TypedValue typedValue = null;

typedArray.getValue(0, typedValue);

At this point the typedArray object should represent the string-array "array01", however, I don't see how to retrieve the "id" and "title" string elements. Any help would be appreciated, thanks in advance.


You can almost do what you want. You have to declare each array separately and then an array of references. Something like this:

<string-array name="array01">
    <item name="id">1</item>
    <item name="title">item one</item>
</string-array>
<!-- etc. -->
<array name="array0">
    <item>@array/array01</item>
    <!-- etc. -->
</array>

Then in your code you do something like this:

Resources res = getResources();
TypedArray ta = res.obtainTypedArray(R.array.array0);
int n = ta.length();
String[][] array = new String[n][];
for (int i = 0; i < n; ++i) {
    int id = ta.getResourceId(i, 0);
    if (id > 0) {
        array[i] = res.getStringArray(id);
    } else {
        // something wrong with the XML
    }
}
ta.recycle(); // Important!


https://developer.android.com/guide/topics/resources/more-resources.html#Color

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <array name="icons">
        <item>@drawable/home</item>
        <item>@drawable/settings</item>
        <item>@drawable/logout</item>
    </array>
    <array name="colors">
        <item>#FFFF0000</item>
        <item>#FF00FF00</item>
        <item>#FF0000FF</item>
    </array>
</resources>

This application code retrieves each array and then obtains the first entry in each array:

Resources res = getResources();
TypedArray icons = res.obtainTypedArray(R.array.icons);
Drawable drawable = icons.getDrawable(0);
TypedArray colors = res.obtainTypedArray(R.array.colors);
int color = colors.getColor(0,0);


according to @Ted Hopp answer, more elegant way:

<integer-array name="id_array">
    <item>1</item>
    <item>2</item>
    <item>3</item>
</integer-array>
<string-array name="name_array">
    <item>name 1</item>
    <item>name 2</item>
    <item>name 3</item>
</string-array>
<array name="array0">
    <item>@array/id_array</item>
    <item>@array/name_array</item>
</array>

make sure sub arrays row count is identical.
enjoy write code to access array cells!
android is still a kid while maintain the ugly "item" tag of the TypedArray "array0".
in my opinion, the most flexible should be:

<array name="array0">
    <integer-array name="id_array">
        <item>1</item>
        <item>2</item>
        <item>3</item>
    </integer-array>
    <string-array name="name_array">
        <item>name 1</item>
        <item>name 2</item>
        <item>name 3</item>
    </string-array>
</array>

but don't do that because that's not android way :)


pros:

  1. auto-generated id
  2. styleable attribute
  3. access other resource reference in xml, compile error if the referred resource miss
  4. compile-time syntax check, compile error if you set wrong data type

.............................................................

  1. declare DataItem class for deserialize

DataListItem.java

class DataListItem {
    @Override
    public String toString() {
        return "DataListItem{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", drawable=" + drawable +
                ", flags=" + flags +
                ", enums=" + enums +
                '}';
    }

    public DataListItem(@IdRes int id, String name, Drawable drawable, int flags, int enums) {
        this.id = id;
        this.name = name;
        this.drawable = drawable;
        this.flags = flags;
        this.enums = enums;
    }

    public int id;
    public String name;
    public Drawable drawable;
    public int flags;
    public int enums;
}
  1. declare styleable attributes

attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:app="http://schemas.android.com/apk/res-auto">
    <declare-styleable name="DataTestItem">
        <attr name="android:id" format="reference"/>
        <attr name="android:drawable" format="reference"/>
        <attr name="android:name" format="string"/>
        <attr name="name2" format="flags">
            <flag name="flag1" value="1"/>
            <flag name="flag2" value="2"/>
            <flag name="flag3" value="4"/>
        </attr>
        <attr name="name3" format="enum">
            <enum name="enum1" value="1"/>
            <enum name="enum2" value="2"/>
            <enum name="enum3" value="3"/>
        </attr>
    </declare-styleable>
</resources>
  1. create xml resource for DataItem list & map

test_list.xml

<?xml version="1.0" encoding="utf-8"?>
<list xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:name="@string/app_name"
        android:id="@+id/data1"
        android:drawable="@drawable/ic_launcher_background"
        app:name2="flag1|flag2"
        app:name3="enum1"/>
    <item
        android:name="@string/app_name"
        android:id="@+id/data2"
        android:drawable="@drawable/ic_launcher_background"
        app:name2="flag2"
        app:name3="enum2"/>
    <item
        android:name="@string/app_name"
        android:id="@+id/data3"
        android:drawable="@drawable/ic_launcher_background"
        app:name2="flag3|flag2"
        app:name3="enum3"/>
</list>

test_map.xml

<?xml version="1.0" encoding="utf-8"?>
<map xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/data1"
        android:drawable="@drawable/ic_launcher_background" />
    <item
        android:id="@+id/data2"
        android:drawable="@drawable/ic_launcher_background" />
    <item
        android:id="@+id/data3"
        android:drawable="@drawable/ic_launcher_background" />
</map>

test_set.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item android:name="@string/app_name" />
    <item android:name="@string/app_name2" />
    <item android:name="@string/app_name" />
    <item android:name="@string/app_name" />
</set>
  1. load DataItem list & map from xml resource

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        try {
            List<DataListItem> dataListItems = loadListFromXml(this,
                    R.xml.test_list,
                    R.styleable.DataTestItem,
                    ArrayList::new,
                    (s) -> new DataListItem(
                            s.getResourceId(R.styleable.DataTestItem_android_id, 0),
                            s.getString(R.styleable.DataTestItem_android_name),
                            s.getDrawable(R.styleable.DataTestItem_android_drawable),
                            s.getInteger(R.styleable.DataTestItem_name2, 0),
                            s.getInteger(R.styleable.DataTestItem_name3, 0)));

            Map<Integer, Drawable> dataMapItem = loadMapFromXML(this,
                    R.xml.test_map,
                    R.styleable.DataTestItem,
                    HashMap::new,
                    (s) -> s.getResourceId(R.styleable.DataTestItem_android_id, 0),
                    (s) -> s.getDrawable(R.styleable.DataTestItem_android_drawable));

            Set<String> dataSetItems = loadSetFromXml(this,
                    R.xml.test_set,
                    R.styleable.DataTestItem,
                    HashSet::new,
                    (s) -> s.getString(R.styleable.DataTestItem_android_name));


            Log.d(TAG, "loadListFromXml return " + dataListItems + ", loadMapFromXML return " + dataMapItem + ", loadSetFromXml return " + dataSetItems);

        } catch (IOException | XmlPullParserException e) {
            Log.w(TAG, "onCreate: fail to parse", e);
        }
    }

    static <T> List<T> loadListFromXml(Context context,
                                       @XmlRes int xml_resid,
                                       @StyleableRes int[] attrs,
                                       Supplier<List<T>> supplier,
                                       Function<TypedArray, ? extends T> inflater) throws IOException, XmlPullParserException {
        return collectFromXml(context,
                xml_resid,
                "list",
                "item",
                attrs,
                supplier,
                (s, ta) -> s.add(inflater.apply(ta)));
    }

    static <T> Set<T> loadSetFromXml(Context context,
                                     @XmlRes int xml_resid,
                                     @StyleableRes int[] attrs,
                                     Supplier<Set<T>> supplier,
                                     Function<TypedArray, ? extends T> inflater) throws IOException, XmlPullParserException {
        return collectFromXml(context,
                xml_resid,
                "set",
                "item",
                attrs,
                supplier,
                (s, ta) -> s.add(inflater.apply(ta)));
    }

    static <K, V> Map<K, V> loadMapFromXML(Context context,
                                           @XmlRes int xml_resid,
                                           @StyleableRes int[] attrs,
                                           Supplier<Map<K, V>> supplier,
                                           Function<TypedArray, ? extends K> keyInflater,
                                           Function<TypedArray, ? extends V> valueInflater) throws IOException, XmlPullParserException {
        return collectFromXml(context,
                xml_resid,
                "map",
                "item",
                attrs,
                supplier,
                (s, ta) -> s.put(keyInflater.apply(ta), valueInflater.apply(ta)));
    }


    static <R> R collectFromXml(Context context,
                                @XmlRes int xml_resid,
                                @Nullable String rootNodeName,
                                @Nullable String itemNodeName,
                                @StyleableRes int[] attrs,
                                Supplier<R> supplier,
                                BiConsumer<R, TypedArray> accumulator) throws IOException, XmlPullParserException {
        try (XmlResourceParser xml = context.getResources().getXml(xml_resid)) {
            final R ret = supplier.get();
            if (xml.next() == START_DOCUMENT) {
                if (xml.nextTag() == START_TAG && ((rootNodeName == null) || rootNodeName.equals(xml.getName()))) {
                    while (xml.nextTag() == START_TAG && ((itemNodeName == null) || itemNodeName.equals(xml.getName()))) {
                        final TypedArray ta = context.obtainStyledAttributes(xml, attrs);
                        accumulator.accept(ret, ta);
                        ta.recycle();
                        if (xml.nextTag() != END_TAG) {
                            break;
                        }
                    }
                }
            }
            return ret;
        }
    }
}


An Array isn't a name/value pair. You only can access the elements by number. The syntax in the xml is wrong. It should be that way:

<string-array name="string_array_name">
    <item>text_string1</item>
    <item>text_string2</item>
 </string-array>
0

精彩评论

暂无评论...
验证码 换一张
取 消