Search code examples
javaandroidreflectiondeclare-styleable

How to get values from declare styleable TypedArray via reflection ?


I was using shape image view (https://github.com/siyamed/android-shape-imageview), and when I want to get value from declare-styleable that the shape image view define:

<declare-styleable name="ShaderImageView">
    <attr name="siSquare" format="boolean" />
    <attr name="siBorderColor" format="color" />
    <attr name="siBorderWidth" format="dimension" />
    <attr name="siBorderAlpha" format="float" />
    <attr name="siForeground" format="integer|reference" />
    <!--  Rounded Image View-->
    <attr name="siRadius" format="dimension" />
    <!-- BubbleImageView-->
    <attr name="siArrowPosition" />
    <attr name="siTriangleHeight" format="dimension" />
    <!-- PorterImageView-->
    <attr name="siShape" format="integer|reference" />
    <!-- ShaderImageView-->
    <attr name="siBorderType" />
    <attr name="siStrokeCap" />
    <attr name="siStrokeJoin" />
    <attr name="siStrokeMiter" format="dimension" />
</declare-styleable>
<attr name="siArrowPosition">
    <enum name="left" value="0" />
    <enum name="right" value="1" />
</attr>
<attr name="siBorderType">
    <enum name="stroke" value="0" />
    <enum name="fill" value="1" />
</attr>
<attr name="siStrokeCap">
    <enum name="butt" value="0" />
    <enum name="round" value="1" />
    <enum name="square" value="2" />
</attr>
<attr name="siStrokeJoin">
    <enum name="bevel" value="0" />
    <enum name="miter" value="1" />
    <enum name="round" value="2" />
</attr>

I got problem. My xml usage of shape image view:

<com.demo.example.BubbleImageView
            android:id="@+id/picture_iv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:siArrowPosition="right"
            android:layout_gravity="center"
            app:siBorderWidth="@dimen/default_border_width"
            app:siRadius="5dp"
            android:src="@drawable/fetch_failed"/>

Here is my code:

public void init(Context context, AttributeSet attrs, int defStyle) {
    if(attrs != null){
        int[] declareStyleableArray = IdHelper.getResourceDeclareStyleableIntArray(context, "ShaderImageView");
        if (declareStyleableArray != null && declareStyleableArray.length > 0) {
            TypedArray typedArray = context.obtainStyledAttributes(attrs, declareStyleableArray, defStyle, 0);
            square = typedArray.getBoolean(IdHelper.getAttr(context, "ShaderImageView_siSquare"), square);
            borderColor = typedArray.getColor(IdHelper.getAttr(context, "ShaderImageView_siBorderColor"), borderColor);
            borderWidth = typedArray.getDimensionPixelSize(IdHelper.getAttr(context, "ShaderImageView_siBorderWidth"), borderWidth);
            borderAlpha = typedArray.getFloat(IdHelper.getAttr(context, "ShaderImageView_siBorderAlpha"), borderAlpha);
            typedArray.recycle();
        }

    }

The method defined in IdHelper:

public static int[] getResourceDeclareStyleableIntArray(Context context, String name) {
    try {
        //use reflection to access the resource class
        Field[] fields2 = Class.forName(context.getPackageName() + ".R$styleable").getFields();

        //browse all fields
        for (Field f : fields2) {
            //pick matching field
            if (f.getName().equals(name)) {
                //return as int array
                return (int[]) f.get(null);
            }
        }
    } catch (Throwable t) {
        t.printStackTrace();
    }

    return null;
}
public static int getAttr(Context context, String attrName) {
    return context.getResources().getIdentifier(attrName, "attr",
            context.getApplicationContext().getPackageName());
}

When I run my application, I got the log:

01-11 17:04:07.244 3744-3744/? W/System.err: Caused by: java.lang.reflect.InvocationTargetException 01-11 17:04:07.247 3744-3744/? W/System.err: at java.lang.reflect.Constructor.constructNative(Native Method) 01-11 17:04:07.247 3744-3744/? W/System.err: at java.lang.reflect.Constructor.newInstance(Constructor.java:423) 01-11 17:04:07.247 3744-3744/? W/System.err: at android.view.LayoutInflater.createView(LayoutInflater.java:594) 01-11 17:04:07.247 3744-3744/? W/System.err: ... 57 more 01-11 17:04:07.247 3744-3744/? W/System.err: Caused by: java.lang.UnsupportedOperationException: Can't convert to dimension: type=0x10 01-11 17:04:07.252 3744-3744/? W/System.err: at android.content.res.TypedArray.getDimensionPixelSize(TypedArray.java:464) 01-11 17:04:07.252 3744-3744/? W/System.err: at cn.jmessage.android.uikit.chatting.shader.ShaderHelper.init(ShaderHelper.java:80) 01-11 17:04:07.252 3744-3744/? W/System.err: at cn.jmessage.android.uikit.chatting.shader.BubbleShader.init(BubbleShader.java:45) 01-11 17:04:07.253 3744-3744/? W/System.err: at cn.jmessage.android.uikit.chatting.ShaderImageView.setup(ShaderImageView.java:45) 01-11 17:04:07.253 3744-3744/? W/System.err: at cn.jmessage.android.uikit.chatting.ShaderImageView.(ShaderImageView.java:36) 01-11 17:04:07.253 3744-3744/? W/System.err: at cn.jmessage.android.uikit.chatting.BubbleImageView.(BubbleImageView.java:27) 01-11 17:04:07.253 3744-3744/? W/System.err: ... 60 more

How can I solve this problem?


Solution

  • I have solved this problem. The declare-styleable attributes that you define in attrs file, after parsed, an int array will be generated in R file. In my case, the array is like:

           @see #ShaderImageView_siArrowPosition
           @see #ShaderImageView_siBorderAlpha
           @see #ShaderImageView_siBorderColor
           @see #ShaderImageView_siBorderType
           @see #ShaderImageView_siBorderWidth
           @see #ShaderImageView_siForeground
           @see #ShaderImageView_siRadius
           @see #ShaderImageView_siShape
           @see #ShaderImageView_siSquare
           @see #ShaderImageView_siStrokeCap
           @see #ShaderImageView_siStrokeJoin
           @see #ShaderImageView_siStrokeMiter
           @see #ShaderImageView_siTriangleHeight
         */
        public static final int[] ShaderImageView = {
            0x7f010001, 0x7f010002, 0x7f010003, 0x7f010004,
            0x7f01000c, 0x7f01000d, 0x7f01000e, 0x7f01000f,
            0x7f010010, 0x7f010011, 0x7f010012, 0x7f010013,
            0x7f010014
        };
    

    Pay attention at the order of this array, is in ascending order according to the letter, so I can use this way to get the attribute values:

    public void init(Context context, AttributeSet attrs, int defStyle) {
        if(attrs != null){
            int[] declareStyleableArray = IdHelper.getResourceDeclareStyleableIntArray(context, "ShaderImageView");
            if (declareStyleableArray != null && declareStyleableArray.length > 0) {
                TypedArray typedArray = context.obtainStyledAttributes(attrs, declareStyleableArray, defStyle, 0);
                square = typedArray.getBoolean(8, square);
                borderColor = typedArray.getColor(2, borderColor);
                borderWidth = typedArray.getDimensionPixelSize(4, borderWidth);
                borderAlpha = typedArray.getFloat(1, borderAlpha);
                typedArray.recycle();
            }
    
        }
    

    Notice this line: square = typedArray.getBoolean(8, square); the first parameter is 8, that's the order of attribute "ShaderImageView_siSquare" in array ShaderImageView which generated in R file. That means if you define a custom declare-styleable set in attrs file, the order you define may not the same as the order generated in R file after parsed, if you use R to get the attribute value like:

    square = typedArray.getBoolean(R.styleable.ShaderImageView_siSquare, square);
    

    then the order is no need to concern. Hope my case will be helpful for someone.