obtainStyledAttributes works wrong

1.4k views Asked by At

I am trying to create my own keyboard. Using Android Keyboard is not enough for my task so I decided to create inheritor class directly from View.class. As base I decided to use code of Keyboard.class and then start change things one by one.

I faced many problems trying even compile that class, used some reflection and hacks to obtain private fields. I created file attrs to be able to use Keyboard's attributes and tags. Finally class compilled and know it crashes becouse parameteres, that should go from inflating, are null.

What I found out is that method context.obtainStyledAttributes(attrs, R.styleable.MyKeyboardView); returns wrong result and a.getIndexCount(); after that returns 0.

here is my attr.xml file:

<resources>
<declare-styleable name="MyKeyboardView">
    <attr name="keyBackground" format="reference" />
    <attr name="keyTextSize" format="dimension" />
    <attr name="labelTextSize" format="dimension" />
    <attr name="state_long_pressable" format="boolean" />
    <attr name="keyTextColor" format="color" />
    <attr name="keyPreviewLayout" format="reference" />
    <attr name="keyPreviewOffset" format="dimension" />
    <attr name="keyPreviewHeight" format="dimension" />
    <attr name="verticalCorrection" format="dimension" />
    <attr name="shadowColor" format="color" />
    <attr name="shadowRadius" format="float" />
    <attr name="backgroundDimAmount" format="float" />
    <attr name="popupLayout" format="reference" />
</declare-styleable>

here is part of my keyboard class:

public MyKeyboardView(Context context, AttributeSet attrs) {
    this(context, attrs, Resources.getSystem().getIdentifier("keyboardViewStyle", "attr", "android"));
}

public MyKeyboardView(Context context, AttributeSet attrs, int defStyleAttr) {
    this(context, attrs, defStyleAttr, 0);
}

public MyKeyboardView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);

    TypedArray a = context.obtainStyledAttributes(
            attrs, R.styleable.MyKeyboardView);

    LayoutInflater inflate =
            (LayoutInflater) context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    int previewLayout = 0;
    int keyTextSize = 0;

    int n = a.getIndexCount();

This is how I use keyboard in layout:

<FrameLayout
    android:layout_width="wrap_content"
    android:layout_height="240dp"
    android:layout_gravity="bottom">
    <com.test.features.keyboard.MyKeyboardView
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/keyboard_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingTop="2.5dip"
        android:keyPreviewHeight="@dimen/key_height"
        android:shadowRadius="0"
        android:keyPreviewLayout="@layout/keyboard_key_preview"
        android:keyPreviewOffset="0dp"
        android:keyBackground="@drawable/keyboard_key_background"
        android:keyTextColor="@color/keyboard_key_color"
        android:background="@color/primary"
        android:popupLayout="@layout/keyboard_popup_layout"/>

tried to use it like so, length() returned 11, but switch case found almost no matches. And if found, default value was used:

int n = a.length();

    for (int i = 0; i < n; i++) {
        int attr = a.getIndex(i);

        switch (attr) {
            case R.styleable.MyKeyboardView_keyBackground:
                mKeyBackground = a.getDrawable(attr);
                break;
            case R.styleable.MyKeyboardView_verticalCorrection:
                mVerticalCorrection = a.getDimensionPixelOffset(attr, 0);
                break;
            case R.styleable.MyKeyboardView_keyPreviewLayout:
                previewLayout = a.getResourceId(attr, 0);
                break;
            case R.styleable.MyKeyboardView_keyPreviewOffset:
                mPreviewOffset = a.getDimensionPixelOffset(attr, 0);
                break;
            case R.styleable.MyKeyboardView_keyPreviewHeight:
                mPreviewHeight = a.getDimensionPixelSize(attr, 80);
                break;
            case R.styleable.MyKeyboardView_keyTextSize:
                mKeyTextSize = a.getDimensionPixelSize(attr, 18);
                break;
            case R.styleable.MyKeyboardView_keyTextColor:
                mKeyTextColor = a.getColor(attr, 0xFF000000);
                break;
            case R.styleable.MyKeyboardView_labelTextSize:
                mLabelTextSize = a.getDimensionPixelSize(attr, 14);
                break;
            case R.styleable.MyKeyboardView_popupLayout:
                mPopupLayout = a.getResourceId(attr, 0);
                break;
            case R.styleable.MyKeyboardView_shadowColor:
                mShadowColor = a.getColor(attr, 0);
                break;
            case R.styleable.MyKeyboardView_shadowRadius:
                mShadowRadius = a.getFloat(attr, 0f);
                break;
        }
    }
2

There are 2 answers

0
Mike M. On BEST ANSWER

Your custom View attributes are going to be in your app's namespace, not the system namespace, so they weren't found in the given AttributeSet when they had the system namespace prefix on them.

Add the xmlns:app="http://schemas.android.com/apk/res-auto" namespace declaration either directly in the <MyKeyboardView> tag, or in an ancestor tag, and change the prefix on all of your attributes to app.

Note that the prefix can be any valid name you like, though app is probably the most common.

1
Alim S. Ajiev On

If you do not need your attributes being styleable You may not use "declare-styleable" at all. Just put your attributes in "com.test.features.keyboard.MyKeyboardView" tag without any namespace prefix and get their values like this:

attrs.getAttributeIntValue( null, "your_attribute", 0 );