Android : How to update the selector(StateListDrawable) programmatically - android

I want to update the selector for a button programmatically.
I can do this with the xml file which is given below
<?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:drawable="#drawable/btn_off" />
<item android:state_pressed="true"
android:state_enabled="true"
android:drawable="#drawable/btn_off" />
<item android:state_focused="true"
android:state_enabled="true"
android:drawable="#drawable/btn_on" />
<item android:state_enabled="true"
android:drawable="#drawable/btn_on" />
</selector>
I want to do the same thing programmatically. I have tried something like given below
private StateListDrawable setImageButtonState(int index)
{
StateListDrawable states = new StateListDrawable();
states.addState(new int[] {android.R.attr.stateNotNeeded},R.drawable.btn_off);
states.addState(new int[] {android.R.attr.state_pressed, android.R.attr.state_enabled},R.drawable.btn_off);
states.addState(new int[] {android.R.attr.state_focused, android.R.attr.state_enabled},R.drawable.btn_on);
states.addState(new int[] {android.R.attr.state_enabled},R.drawable.btn_on);
return states;
}
but it didnt work.
And how to set android:state_enabled="false" or android:state_enabled="true" programatically.

You need to use the negative value of the needed state.
E.g.:
states.addState(new int[] {-android.R.attr.state_enabled},R.drawable.btn_disabled);
Notice the "-" sign before android.R.attr.state_enabled.

Very late response here but in case anyone else is having problems setting a StateListDrawable programmatically. Then as with the XML files the order in which you set the states into the StateListDrawable is important.
For example this will work as expected:
StateListDrawable sld = new StateListDrawable();
sld.addState(new int[] { android.R.attr.state_pressed }, new ColorDrawable(Color.GRAY));
sld.addState(new int[] {}, new ColorDrawable(Color.GREEN));
This won't:
StateListDrawable sld = new StateListDrawable();
sld.addState(new int[] {}, new ColorDrawable(Color.GREEN));
sld.addState(new int[] { android.R.attr.state_pressed }, new ColorDrawable(Color.GRAY));

I am going to answer your question "How to update the selector for a BUTTON programmatically?" by proposing to switch a button for a LinearLayout with embedded ImageView and TextView. There are a number of benefits to doing this, especially if you will later decide to customize your views. There is no loss of functionality resulting from this switch. You will still be able to attach same event listeners you can attach to a button, but will be able to avoid the buttons/tabs styling nightmares. Here is a relevant code from the layout.xml
<LinearLayout
android:id="#+id/button"
style="#style/ButtonStyle">
<ImageView
android:id="#+id/background"
android:src="#drawable/custom_image"/>
<TextView
style="#style/TextStyle"
android:text="Custom Button"
android:id="#+id/text"/>
</LinearLayout>
Next, I have a selector file called custom_image.xml located in the drawable folder. Here is the content of the selector file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/disabled_img" android:state_enabled="false" />
<item android:drawable="#drawable/unselected_img" android:state_selected="false" />
<item android:drawable="#drawable/selected_img" android:state_selected="true" />
</selector>
The three source image files (disabled_img.png, unselected_img.png, selected_img.png) are also located in the drawable folder.
Now back to your Java code. There is no need for the funky StateListDrawable garbage for many reasons. First, it just looks ugly, and is hard to maintain. But most importantly it goes against the principles of keeping your logic separate from your presentation. If you are managing your drawable resources in Java code, you know you are doing something fundametally wrong.
Here is what I am proposing instead. Whenever you want your button to be selected, you just pop this one-liner in there:
((LinearLayout)findViewById(R.id.button)).setSelected(true);
Or whenever you want the button to be in the disabled state, here is another one-liner:
((ImageView)findViewById(R.id.background)).setEnabled(false);
Please notice that in this last example I am specifying the disabled state on the ImageView inside the LinearLayout. For some reason whenever you change the enabled / disabled state of the LinearLayout, the selector is not being triggered. It works fine when you do it on the ImageView instead.

Not enough rep to comment but the accepted answer did not work for me.
My goal was for designers to set enabled, disabled, and pressed background colors on some button. I intended for them to use it to test colors on different displays.
I noticed some other people mentioned it did not work for them either.
The states that need to be defined are
not pressed and enabled, this is what you see when the button is enabled and not pressed.
pressed and enabled, this is what you see when the button is pressed and enabled
not enabled. This is what you see when the button is disabled
Here is some code that will give you an idea of the states. I am using Color.Parse() to generate ints for the colors then I pass them to this method to get the StateListDrawable.
private StateListDrawable createDrawable(int enabled, int pressed, int disabled) {
StateListDrawable stateListDrawable = new StateListDrawable();
stateListDrawable.addState(new int[] { -android.R.attr.state_pressed, android.R.attr.state_enabled }, new ColorDrawable(enabled));
stateListDrawable.addState(new int[] { android.R.attr.state_pressed, android.R.attr.state_enabled }, new ColorDrawable(pressed));
stateListDrawable.addState(new int[] { -android.R.attr.state_enabled }, new ColorDrawable(disabled));
return stateListDrawable;
}

I don't know how you are adding the StateListDrawable, since the code is not here. But be sure to be check the documentation and the adding the setState().
You can set the properties from the View, such as yourView.setEnabled(true)
I hope that helps

Related

Setting StateDrawableList to ImageButton in code have different results from setting via xml?

I have next result:
result
Right buttons:
android:adjustViewBounds="true"
android:background="#android:color/transparent"
android:scaleType="centerInside"
android:src="#drawable/xml_menu_button"
xml_menu_button:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_pressed="false" android:drawable="#drawable/bottom_button_menu"></item>
<item android:state_pressed="true" android:drawable="#drawable/bottom_button_menu_pressed"></item>
</selector>
Second button have similar xml as config.
Left buttons added to the panel dynamically. So I set its resources via code:
ImageButton button = new ImageButton(getActivity());
button.setBackgroundColor(Color.TRANSPARENT);
button.setAdjustViewBounds(true);
button.setScaleType(ScaleType.CENTER_INSIDE);
StateListDrawable states = new StateListDrawable();
states.addState(new int[] {android.R.attr.state_pressed},
new BitmapDrawable(getResources(), PATH_TO_BUTTON_PRESSED_IMAGE));
states.addState(new int[] { },
new BitmapDrawable(getResources(), UPATH_TO_BUTTON_IMAGE));
button.setImageDrawable(states);
Panel is custom ViewGroup. With layout bounds turned on, I can see, that measuring and layout stage passed as I want. But there is a difference between image sizes, as you can see.
All buttons have the same size 70x58 px. In layout buttons have size 77x77.
What am doing wrong?

Changing colors to custom button

I have a custom button in my drawable folder.
I want to use it multiple times in my XML file, with different colors.
Is there a way to use the same custom_button but with different color?
Firstly create a selector xml file .Selector will let you change button image on specific states like focused or pressed .
I am giving you the sample code for selector
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="#drawable/ok_pressed"
android:state_pressed="true" />
<item android:drawable="#drawable/ok" />
</selector>
Now using StateListDrawable you can assign a number of graphic images to a single Drawable and swap out the visible item by a string ID value.
StateListDrawable state_up = new StateListDrawable();
state_up.addState(new int[] {android.R.attr.state_pressed},getResources().getDrawable(R.drawable.btn_up_cyan));
state_up.addState(new int[] {android.R.attr.state_focused},getResources().getDrawable(R.drawable.btn_up_cyan));
b1.setBackgroundDrawable(state_up);
Just use different color images as background and use the above code when you wish to change the background

Change parent's background color when its child is clicked

I have a TableRow which contains a LinearLayout, and then this LinearLayout contains a TextView. What I want is, once the TextView is clicked, the entire TableRow would change its background color.
I've tried to use getParent() and performClick() to pass the click event from TextView to TableRow. The onClick() method of TableRow does get called, but its background color does not change.
Of course I've set the selector by using either
row.setBackgroundResource(R.drawable.menu_item_bgcolor);
or
row.setBackgroundDrawable(activity.getResources().getDrawable(R.drawable.menu_item_bgcolor));
Doesn't work. Can anyone provide any insight into this? Thanks,
Below is the selector xml file:
<?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/menu_item_pressed" />
<item android:state_focused="true" android:drawable="#drawable/menu_item_pressed" />
<item android:drawable="#drawable/menu_item_normal" />
</selector>
try this
StateListDrawable states = new StateListDrawable();
states.addState(new int[] {android.R.attr.state_pressed},
getResources().getDrawable(R.drawable.pressed));
states.addState(new int[] {android.R.attr.state_focused},
getResources().getDrawable(R.drawable.focused));
states.addState(new int[] { },
getResources().getDrawable(R.drawable.normal));
row.setImageDrawable(states);

Android button states programmatically in Java (not XML)

How does one define Android button image for the "state_pressed"
"android:state_focused" in Java?
For example, how would one accomplish the equivalent in Java for the XML from
http://developer.android.com/reference/android/widget/ImageButton.html
<?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" /> <!-- pressed -->
<item android:state_focused="true"
android:drawable="#drawable/button_focused" /> <!-- focused -->
<item android:drawable="#drawable/button_normal" /> <!-- default -->
</selector>
Just use addState method of StateListDrawable
StateListDrawable stateListDrawable = new StateListDrawable();
stateListDrawable.addState(new int[] {android.R.attr.state_pressed},
getResources().getDrawable(R.drawable.phone));
You can use constants below for the first parameter of this method
android.R.attr.state_accelerated
android.R.attr.state_activated
android.R.attr.state_active
android.R.attr.state_drag_can_accept
android.R.attr.state_drag_hovered
android.R.attr.state_enabled
android.R.attr.state_first
android.R.attr.state_focused
android.R.attr.state_hovered
android.R.attr.state_last
android.R.attr.state_middle
android.R.attr.state_pressed
android.R.attr.state_selected
android.R.attr.state_single
android.R.attr.state_window_focused
Create an instance of StateListDrawable and then assign it with imagebutton.setImageDrawable(stateDrawable).
10x to Tang Ke, I`m using this for different list item color whit selection color.
selected state
stateListDrawable.addState(new int[] {android.R.attr.state_pressed},
new ColorDrawable(getResources().getColor(R.color.alpha_blue500)));
default state
stateListDrawable.addState(new int[] {},
new ColorDrawable(getResources().getColor(R.color.red)));
Here you can change color for different state of the row item (ex. paid vs free)
set state to custom layout row item in list adapter
holder.relativeLayout.setBackgroundDrawable(stateListDrawable);

How to modify the default button state in Android without affecting the pressed and selected states?

I am trying to remove an ImageButton's background in only the default state. I'd like the pressed and selected states to behave as usual so that they look correct on different devices, which use different colors for the pressed and selected states.
Is there any way to set an ImageButton's background default state's drawable without affecting the pressed and selected states?
I've tried to do this with a selector, but it does not appear to allow you to use the default drawables for some states - you have to set all the states yourself. Since there's no API to retrieve the device's default pressed/selected drawables, I don't know what to set the pressed/selected states to.
I also tried getting the StateListDrawable object for the button that the system creates when you are not using a selector and then modify it change the default state. That didn't work either.
I seems that on Android, if you want to change the drawable for one state of a button, then you must set all the states, and thus cannot preserve the default drawables for the other states. Is this correct?
Thanks!
-Tom B.
Tom,
It's true that if you override the default state you also have to override the pressed and focused states. The reason is that the default android drawable is a selector, and so overriding it with a static drawable means that you lose the state information for pressed and focused states as you only have one drawable specified for it. It's super easy to implement a custom selector, though. Do something like this:
<selector
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/custombutton">
<item
android:state_focused="true"
android:drawable="#drawable/focused_button" />
<item
android:state_pressed="true"
android:drawable="#drawable/pressed_button" />
<item
android:state_pressed="false"
android:state_focused="false"
android:drawable="#drawable/normal_button" />
</selector>
Put this in your drawables directory, and load it like a normal drawable for the background of the ImageButton. The hardest part for me is designing the actual images.
Edit:
Just did some digging into the source of EditText, and this is how they set the background drawable:
public EditText(/*Context context, AttributeSet attrs, int defStyle*/) {
super(/*context, attrs, defStyle*/);
StateListDrawable mStateContainer = new StateListDrawable();
ShapeDrawable pressedDrawable = new ShapeDrawable(new RoundRectShape(10,10));
pressedDrawable.getPaint().setStyle(Paint.FILL);
pressedDrawable.getPaint().setColor(0xEDEFF1);
ShapeDrawable focusedDrawable = new ShapeDrawable(new RoundRectShape(10,10));
focusedDrawable.getPaint().setStyle(Paint.FILL);
focusedDrawable.getPaint().setColor(0x5A8AC1);
ShapeDrawable defaultDrawable = new ShapeDrawable(new RoundRectShape(10,10));
defaultDrawable.getPaint().setStyle(Paint.FILL);
defaultDrawable.getPaint().setColor(Color.GRAY);
mStateContainer.addState(View.PRESSED_STATE_SET, pressedDrawable);
mStateContainer.addState(View.FOCUSED_STATE_SET, focusedDrawable);
mStateContainer.addState(StateSet.WILD_CARD, defaultDrawable);
this.setBackgroundDrawable(mStateContainer);
}
I'm sure you could adapt the idea to your purposes. Here is the page I found it on:
http://www.google.com/codesearch/p?hl=en#ML2Ie1A679g/src/android/widget/EditText.java
StateListDrawable replace = new StateListDrawable();
Drawable old = getBackground();
replace.addState(FOCUSED_STATE_SET, old);
replace.addState(SELECTED_STATE_SET, old);
replace.addState(PRESSED_STATE_SET, old);
replace.addState(StateSet.WILD_CARD, new ColorDrawable(Color.TRANSPARENT));
if (Build.VERSION.SDK_INT >= 16) {
setBackground(replace);
} else {
setBackgroundDrawable(replace);
}

Categories

Resources