90

If I was using an ImageButton with a selector for its background, is there a state I can change which will make it change its appearance? Right now I can get it to change images when pressed, but there seems to be no "highlighted" or "selected" or similar state which lets me toggle its appearance at will.

Here's my XML; it only changes appearance when pressed.

 <selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:state_pressed="false" android:drawable="@drawable/map_toolbar_details_selected" />
<item android:state_focused="true" android:state_pressed="true" android:drawable="@drawable/map_toolbar_details_selected" />
<item android:state_focused="false" android:state_pressed="true" android:drawable="@drawable/map_toolbar_details_selected" />
<item android:drawable="@drawable/map_toolbar_details" />

Carl Manaster
  • 39,912
  • 17
  • 102
  • 155
Joren
  • 9,623
  • 19
  • 63
  • 104

6 Answers6

223

This works for me:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- NOTE: order is important (the first matching state(s) is what is rendered) -->
    <item 
        android:state_selected="true" 
        android:drawable="@drawable/info_icon_solid_with_shadow" />
    <item 
        android:drawable="@drawable/info_icon_outline_with_shadow" />
 </selector>

And then in java:

//assign the image in code (or you can do this in your layout xml with the src attribute)
imageButton.setImageDrawable(getBaseContext().getResources().getDrawable(R.drawable....));

//set the click listener
imageButton.setOnClickListener(new OnClickListener() {

    public void onClick(View button) {
        //Set the button's appearance
        button.setSelected(!button.isSelected());

        if (button.isSelected()) {
            //Handle selected state change
        } else {
            //Handle de-select state change
        }

    }

});

For smooth transition you can also mention animation time:

<selector xmlns:android="http://schemas.android.com/apk/res/android" android:exitFadeDuration="@android:integer/config_mediumAnimTime">
AZ_
  • 21,688
  • 25
  • 143
  • 191
joshrl
  • 3,527
  • 1
  • 22
  • 12
  • 3
    This worked for me, but I would like to add, that one should create this xml in your drawable folder and if you want ImageButton to highlight when it pressed, you should use android:state_pressed rather than state_selected. Also, this state should be above an item without state, since it is less general one. – Denis Kutlubaev Dec 16 '13 at 17:46
  • 3
    `NOTE: order is important (the first matching state(s) is what is rendered` this did worked but strange i didn't get WHY? – Muhammad Babar Jan 03 '15 at 09:22
  • 2
    In the absence of any state attributes, the item will match any state. So think of the second item in the selector as the "catch all" state. – joshrl Jan 22 '15 at 18:47
  • 2
    The order is not important you only need to make sure you provide `android:state_selected="false" ` with the default one! – Muhammad Babar May 05 '15 at 10:46
  • How can I do the same in a widget? I have a button but I can't seem to figure out how to do it. – Si8 Nov 19 '16 at 14:43
20

ToggleImageButton which implements Checkable interface and supports OnCheckedChangeListener and android:checked xml attribute:

public class ToggleImageButton extends ImageButton implements Checkable {
    private OnCheckedChangeListener onCheckedChangeListener;

    public ToggleImageButton(Context context) {
        super(context);
    }

    public ToggleImageButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        setChecked(attrs);
    }

    public ToggleImageButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setChecked(attrs);
    }

    private void setChecked(AttributeSet attrs) {
        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ToggleImageButton);
        setChecked(a.getBoolean(R.styleable.ToggleImageButton_android_checked, false));
        a.recycle();
    }

    @Override
    public boolean isChecked() {
        return isSelected();
    }

    @Override
    public void setChecked(boolean checked) {
        setSelected(checked);

        if (onCheckedChangeListener != null) {
            onCheckedChangeListener.onCheckedChanged(this, checked);
        }
    }

    @Override
    public void toggle() {
        setChecked(!isChecked());
    }

    @Override
    public boolean performClick() {
        toggle();
        return super.performClick();
    }

    public OnCheckedChangeListener getOnCheckedChangeListener() {
        return onCheckedChangeListener;
    }

    public void setOnCheckedChangeListener(OnCheckedChangeListener onCheckedChangeListener) {
        this.onCheckedChangeListener = onCheckedChangeListener;
    }

    public static interface OnCheckedChangeListener {
        public void onCheckedChanged(ToggleImageButton buttonView, boolean isChecked);
    }
}

res/values/attrs.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="ToggleImageButton">
        <attr name="android:checked" />
    </declare-styleable>
</resources>
vokilam
  • 10,153
  • 3
  • 45
  • 56
  • 2
    How can I use it? I followed the instruction in: http://developer.android.com/training/custom-views/create-view.html but I couldn't make it work. – atisman Sep 18 '13 at 00:31
  • 3
    Thanks! Note that a selector with `state_checked`does not work with this though, you have to use `state_selected`. – Florian von Stosch Dec 05 '13 at 14:11
11

The best way to do this without more images :

public static void buttonEffect(View button){
    button.setOnTouchListener(new OnTouchListener() {

        public boolean onTouch(View v, MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN: {
                    v.getBackground().setColorFilter(0xe0f47521,PorterDuff.Mode.SRC_ATOP);
                    v.invalidate();
                    break;
                }
                case MotionEvent.ACTION_UP: {
                    v.getBackground().clearColorFilter();
                    v.invalidate();
                    break;
                }
            }
            return false;
        }
    });
}
András
  • 3,395
  • 1
  • 21
  • 27
3

Create an XML-file in a res/drawable folder. For instance, "btn_image.xml":

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/bg_state_1"
          android:state_pressed="true"
          android:state_selected="true"/>
    <item android:drawable="@drawable/bg_state_2"
          android:state_pressed="true"
          android:state_selected="false"/>
    <item android:drawable="@drawable/bg_state_selected"
          android:state_selected="true"/>
    <item android:drawable="@drawable/bg_state_deselected"/>
</selector>

You can combine those files you like, for instance, change "bg_state_1" to "bg_state_deselected" and "bg_state_2" to "bg_state_selected".

In any of those files you can write something like:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">
    <solid android:color="#ccdd00"/>
    <corners android:radius="5dp"/>
</shape>

Create in a layout file an ImageView or ImageButton with the following attributes:

<ImageView
    android:id="@+id/image"
    android:layout_width="50dp"
    android:layout_height="50dp"
    android:adjustViewBounds="true"
    android:background="@drawable/btn_image"
    android:padding="10dp"
    android:scaleType="fitCenter"
    android:src="@drawable/star"/>

Later in code:

image.setSelected(!image.isSelected());
CoolMind
  • 26,736
  • 15
  • 188
  • 224
2

Try this:

 <item
   android:state_focused="true"
   android:state_enabled="true"
   android:drawable="@drawable/map_toolbar_details_selected" />

Also for colors i had success with

<selector
        xmlns:android="http://schemas.android.com/apk/res/android">
        <item
            android:state_selected="true"

            android:color="@color/primary_color" />
        <item
            android:color="@color/secondary_color" />
</selector>
Alex Volovoy
  • 67,778
  • 13
  • 73
  • 54
  • Hmm. Doesn't seem to help. I'm using a CompoundButton. Is there a state specific to that type of button for on\off? – Joren Apr 10 '10 at 00:18
  • Yes it is called checked see _isChecked()_ http://developer.android.com/reference/android/widget/CompoundButton.html – Bostwickenator Jan 10 '13 at 22:39
0
if (iv_new_pwd.isSelected()) {
                iv_new_pwd.setSelected(false);
                Log.d("mytag", "in case 1");
                edt_new_pwd.setInputType(InputType.TYPE_CLASS_TEXT);
            } else {
                Log.d("mytag", "in case 1");
                iv_new_pwd.setSelected(true);
                edt_new_pwd.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
            }
MEGHA DOBARIYA
  • 1,622
  • 9
  • 7