33

This problem has been solved, see comments for details.

I am extending an existing Android View and loading some custom attributes, as described in Declaring a custom android UI element using XML and Defining custom attrs.

Attributes with boolean and integer formats work fine, but when I try to specify a reference to an array resource, the application crashes at launch. I have defined an integer array inside an xml resource file and I'm trying to use it as an attribute for the custom view.

I can use the array resource to set the "entries" attribute of the android Spinner class with no errors, so it seems to be a problem in my implementation. The logcat messages don't seem to supply any specific information about the crash, but I'm still looking so I will update if I find something.

The attributes are declared by (in attrs.xml):

<declare-styleable name="CustomView">
    <attr name="values" format="reference"/>
    <attr name="isActive" format="boolean"/>
</declare-styleable>

The array is defined as (in arrays.xml):

<integer-array name="nums">
    <item>1</item>
    <item>2</item>
    <item>3</item>
</integer-array>

And I am referencing the array by:

<com.test.CustomView cv:values="@array/nums" />

And this causes the application to crash immediately. In addition, if I reference a color resource instead of an array then the application does not crash. Does anybody know how to deal with this problem?

Community
  • 1
  • 1
Michael
  • 331
  • 1
  • 3
  • 4
  • 1
    This problem is solved, it was an error in the way I was loading the attributes in the class constructor. Previously, I was using the getInt() method of TypedArray when I should have been using getResourceId() with the "reference" format. – Michael Oct 30 '11 at 02:51

3 Answers3

48

Just going to piggyback off your question here, since your post shows up first if you google something like "array reference XML attribute custom view", so someone might find this helpful.

If you want your custom view to reference an array of strings, you can use Android's existing android:entries XML attribute, instead of creating a totally new custom attribute.

Just do the following in res/values/attrs.xml:

<resources>
    <declare-styleable name="MyCustomView">
        <attr name="android:entries" />
    </declare-styleable>
</resources>

Then do this in your custom View's constructor:

public MyCustomView(Context context, AttributeSet attrs, int defStyle)
{
    super(context, attrs, defStyle);

    TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MyCustomView, defStyle, 0);
    try
    {
        CharSequence[] entries = a.getTextArray(R.styleable.MyCustomView_android_entries);
        if (entries != null)
        {
           //do something with the array if you want
        }
    }
    finally
    {
        a.recycle();
    }
}

And then you should be able to reference a string array via the android:entries attribute when you add your custom View to an XML layout file. Example:

<com.example.myapp.MyCustomView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:entries="@array/my_array_of_strings" />

This is exactly how it is done in the ListView class (look in the source, you'll see).

Clyde
  • 6,776
  • 3
  • 28
  • 50
XåpplI'-I0llwlg'I -
  • 18,987
  • 23
  • 96
  • 146
19

The other answer works well with array of strings. However, arr.getTextArray(...) on array of references, e.g.

<array name="tmp">
  <item>@drawable/a</item>
  <item>@drawable/b</item>
</array>

will give you res/drawable/a.png as the CharSequence instead of the resource id.

The proper way to parse an array of references is this:

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomView);

int arrayResourceId = typedArray.getResourceId(R.styleable.CustomView_CustomAttr, 0);
if (arrayResourceId != 0) {
    final TypedArray resourceArray = getResources().obtainTypedArray(arrayResourceId);
    for (int i = 0; i < resourceArray.length(); i++) {
        final int resourceId = resourceArray.getResourceId(i, 0);

        // do stuff with resourceId, such as getResources().getDrawable(resourceId)
    }
    resourceArray.recycle();
}
typedArray.recycle();
Johnny Five
  • 788
  • 1
  • 9
  • 27
Jin
  • 5,690
  • 2
  • 34
  • 70
9

The question is about obtain an integer array, for my case, I need to read the colors(int) from an array for my custom view, styeable definition as below:

<declare-styleable name="ColorPickerView">
        <attr name="colors" format="reference" />
    </declare-styleable>

Then I use my custom view like below:

<com.rainliu.colorpicker.ColorPickerView
    android:id="@+id/rtePalette"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    colorPickerView:colors="@array/colorPickerColors"
    />

The colors definition is as below:

<resources>
    <color name="colorPrimary">#FF9800</color>
    <array name="colorPickerColors">
        <item>#000000</item>
        <item>#E65100</item>
        <item>@color/colorPrimary</item>
    </array>
</resources>

So I need to obtain the colors in my custom view (ColorPickerView), code as below:

TypedArray ta = context.obtainStyledAttributes(attributeSet, R.styleable.ColorPickerView);
int colorsId = ta.getResourceId(R.styleable.ColorPickerView_colors, 0);
int[] colorsArray = ta.getResources().getIntArray(colorsId);
for (int a : colorsArray) {
  Log.e("AA", "color == " + a);
}
ta.recycle();

Here is the print of colorsArray:

03-11 14:25:53.894 15300-15300/com.chinalwb.are E/AA: color == -16777216
03-11 14:25:53.894 15300-15300/com.chinalwb.are E/AA: color == -1683200
03-11 14:25:53.894 15300-15300/com.chinalwb.are E/AA: color == -1683200

Hope this will help some guys.

LiuWenbin_NO.
  • 1,012
  • 11
  • 23