Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't understand Android custom drawable state

Tags:

android

I'm new in Android development and I'm writing a small app to understand how it works. I've got all working, but at the moment I can't get a point about custom drawable states... let me explain with some sample code.

Here is my attrs.xml, in which I declare a attribute with name "oddMonth", which is boolean:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="DayView">
        <attr name="oddMonth" format="boolean"/>
    </declare-styleable>
</resources>

Then I have a custom View:

<?xml version="1.0" encoding="utf-8"?>
<com.example.calendar.DayView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="90dp"
    android:background="@drawable/dayview_state" >
    <TextView android:id="@+id/day_number"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_alignParentRight="true"
        android:paddingRight="3dp" />  
</com.example.calendar.DayView>

So I put the line "android:background="@drawable/dayview_state"", which refers to file dayview_state.xml:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:easycalendar="http://schemas.android.com/apk/res/com.example.calendar">

    <item easycalendar:oddMonth ="true" android:drawable="@drawable/customborder_odd" />
    <item easycalendar:oddMonth ="false" android:drawable="@drawable/customborder_even"/>

</selector>

So far... for what I can understand.... I have a attribute defined in attrs.xml. This attribute represents the state for my custom view. According to the boolean value of this attribute my app will load one of two different xml (that are not important here), each of one defines a different drawable. So the final step is to build my custom class! Follows a extract from the class:

public class DayView extends RelativeLayout {
    private static final int[] STATE_ODD_MONTH = { R.attr.oddMonth };
    private boolean mOddmonth = true;

    public DayView(Context mContext, AttributeSet attrs) {
        super(mContext, attrs);
    }

    @Override
    protected int[] onCreateDrawableState(int extraSpace) {
        if (mOddmonth) {
            final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
            mergeDrawableStates(drawableState, STATE_ODD_MONTH);

            return drawableState;
        } else {
            return super.onCreateDrawableState(extraSpace);
        }
    }

    public boolean isOddMonth() {
        return mOddmonth;
    }

    public void setOddMonth(boolean oddMonth) {
        if (mOddmonth != oddMonth) {
            mOddmonth = oddMonth;

            refreshDrawableState();
        }
    }
}

Ok... so I have here a private variable mOddMonth, whith getter and setter. The constructor which is used to inflate this view elsewhere. Another private variable:

private static final int[] STATE_ODD_MONTH = { R.attr.oddMonth };

which is a array made up of only one int value, that is a reference to the attribute oddMonth defined in attrs.xml. And the inherited method:

@Override
    protected int[] onCreateDrawableState(int extraSpace) {
        if (mOddmonth) {
            final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
            mergeDrawableStates(drawableState, STATE_ODD_MONTH);

            return drawableState;
        } else {
            return super.onCreateDrawableState(extraSpace);
        }
    }

which I can't really "deeply" understand... well, it seems to me that I add a state if the local variable mOddMonth is true, otherwise not. So... my code works only if I replace my dayview_state.xml with the following:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:easycalendar="http://schemas.android.com/apk/res/com.example.calendar">

    <item easycalendar:oddMonth ="true" android:drawable="@drawable/customborder_odd" />
    <item android:drawable="@drawable/customborder_even"/>

</selector>

In this way the first layout is loaded if THERE IS the state, otherwise will be loaded the second one. But WHAT ABOUT THE VALUE of the state? Nowhere in my code I set the value for this variable/attribute.... where I'm wrong?

like image 965
Stefano Avatar asked Feb 12 '13 00:02

Stefano


1 Answers

I would recommend you reword your question b/c it wasn't clear what you were asking until I read your comment to @kcoppock's answer, which is -

"what i want to do (or I think I should do) is to set this value somewhere in code according to the actual status of my custom view, and then force it to render again.... Or I shouldn't?"

At any point, you can query the view to get it drawable state using View.getDrawableState.

If based on this, you want to re-render your drawable, then you have several options.

First of all you can call Drawable.invalidateSelf. But you rarely need to do that because usually your drawable is set as a view's background drawable which is automatically drawn for you in the draw method (not onDraw, which is what you draw). So all you need to do in that case is to invalidate the view (view.invalidate), it will automatically redraw your background drawable (hence picking up your drawable state change).

If you are using your drawable not as a background but for your main drawing then you draw your drawables in onDraw. A simple myDrawable.draw(canvas) should be enough. But remember to vall view.invalidate to trigger the onDraw method.

like image 93
numan salati Avatar answered Oct 04 '22 19:10

numan salati