The beginning
when i was about to create a button with different looks than the default, i thought of an ImageButton that will change it's image when it's state changes. i opened the ImageButton reference page and found it to be well explained. basicly we should use an xml file with the following content:
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:drawable="@drawable/button_pressed" /> <item android:state_focused="true" android:drawable="@drawable/button_focused" /> <item android:drawable="@drawable/button_normal" /> </selector>
saved in the res/drawable/ folder. then reference this file in the source property of our ImageButton (android:src).
Done .. works like a charm.
the trouble started when i tried to do the same with the ToggleButton.
first problem: we need to assign a drawable to the checked state of the ToggleButton. i'm looking for an attribute for the checked state; one like ("android:state_pressed"). the place to look for attributes is in the R.attr reference page. i found what i was looking for .. "android:state_checked" .. as expected.
So, I added the following line inside the selector tag:
<item android:state_pressed="true" android:drawable="@drawable/button_pressed" />
second problem: the ToggleButton has no source property (android:src attribute) .. so where are we supposed to reference the selector file?!
i found a android:button attribute for it in its parent CompoundButton that takes a drawable. i tried referencing the selector file there and it worked.
.. but why?
.. here starts our journey ..
<selector> story
what is a selector? .. every tag in the xml layout corresponds to a class in the android skd. i found it to be "StateListDrawable". this class extends the Drawable class.
if that means anything, it means that we can reference a selector file (StateListDrawable) wherever a drawable can be referenced. like what we did in android:background.
time for a little test.
i referenced the selector file in the android:background attribute of a RelativeLayout .. and it worked as expected. the background changed with the state of the RelativeLayout (pressed, focused or normal)!!
but still .. why?
does the RelativeLayout has states? .. why does it has states anyway?!
state_* story
after delving into the sdk reference for a while i developed a vog idea that the View class (the parent of them all) is where these states are defined. the documentation doesn't say much (any to be specefic!) .. so i had a look at the code of the View class. yes,they are defined there.
now i'm sure it's the view class.
the View class changes its state flag according to the user actions. when the view needs to draw itself, it asks the Drawable (or StateListDrwawable) to give it the drawable for the current state.
the View class defines the following states:
- state_enabled
- state_focused
- state_selected
- state_pressed
- state_window_focused
- state_last
- state_first
- state_middle
- state_single
but wait .. where is the state_checked? where is it defined?
i looked for it in the ToggleButton class code and didn't find any. i found it in it's parent the CompoundButton class.
i guess that means that we can write a custom view which defines it's own states like state_foo or state_bar. then create a selector file with these states.
pretty amazingly flexable .. right!!
i'm realy in love with this OS ..
Summary
- the
<selector>tag corresponds to theStateListDrawableclass. this class extends theDrawableclass. - we can reference a selector file wherever a drawable can be referenced.
- we can use a selector file with any type of View (
LinearLayout, Button, TextView..etc) - we can define our own states for our own cutom views (which we will be descussing in a later post).