131

In Android, when I set a background image to a button, I can not see any effect on it when it's clicked.

I need to set some effect on the button, so the user can recognise that the button is clicked.

The button should be dark for a few seconds when it is clicked. How to do this?

Vega
  • 27,856
  • 27
  • 95
  • 103
Jignesh Ansodariya
  • 12,583
  • 24
  • 81
  • 113
  • http://stackoverflow.com/questions/4755871/how-to-set-image-button-backgroundimage-for-different-state/4755934#4755934 – ingsaurabh Aug 24 '11 at 13:05
  • The following method allows you to use a single styleclass to skin all your buttons, regardless of their text: http://stackoverflow.com/questions/5327553/android-highlight-an-imagebutton-when-clicked/5327786#5327786 – Zack Marrapese Aug 24 '11 at 13:10
  • no I am not using Image Button , I am using Normal Button any easy way for this ? – Jignesh Ansodariya Aug 24 '11 at 13:13

17 Answers17

171

This can be achieved by creating a drawable xml file containing a list of states for the button. So for example if you create a new xml file called "button.xml" with the following code:

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

To keep the background image with a darkened appearance on press, create a second xml file and call it gradient.xml with the following code:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    <item>
        <bitmap android:src="@drawable/YOURIMAGE"/>
    </item>
    <item>
        <shape xmlns:android="http://schemas.android.com/apk/res/android">
            <gradient android:angle="90" android:startColor="#880f0f10" android:centerColor="#880d0d0f" android:endColor="#885d5d5e"/>
        </shape>
    </item>
</layer-list>

In the xml of your button set the background to be the button xml e.g.

android:background="@drawable/button"

Changed the above code to show an image (YOURIMAGE) in the button as opposed to a block colour.

Dharman
  • 30,962
  • 25
  • 85
  • 135
Ljdawson
  • 12,091
  • 11
  • 45
  • 60
96

It is simpler when you have a lot of image buttons, and you don't want to write xml-s for every button.

Kotlin Version:

fun buttonEffect(button: View) {
    button.setOnTouchListener { v, event ->
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                v.background.setColorFilter(-0x1f0b8adf, PorterDuff.Mode.SRC_ATOP)
                v.invalidate()
            }
            MotionEvent.ACTION_UP -> {
                v.background.clearColorFilter()
                v.invalidate()
            }
        }
        false
    }
}

Java Version:

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;
        }
    });
}
Edric
  • 24,639
  • 13
  • 81
  • 91
András
  • 3,395
  • 1
  • 21
  • 27
  • 3
    I use this in my code and the only problem I have noticed so far is that the button behaves weirdly when placed in a ScrollView. – Finnboy11 Oct 20 '13 at 07:35
  • It's really superb solution and this should be correct answer – Biraj Zalavadia Jul 02 '14 at 07:28
  • How is this better? Designing should be kept outside of code, shouldn't it? – Bugs Happen Oct 21 '15 at 10:17
  • 1
    Its just a simple solution for this problem. When you have a lots of buttons and you dont want to create xmls for every single one it could be helpful. – András Oct 21 '15 at 11:23
  • Omg! Imagine having 5 buttons in a fragment... You will have whole bunch of this boilerplate code... How to refactor it then? Change colour filter for all the buttons in all the activities? Imho it's your next developer will not be so happy.. – Leo DroidCoder Nov 21 '16 at 08:28
  • It is a function, you can call it multiple times. The color code can be led out into xml – András Nov 21 '16 at 09:49
  • should also be one for MotionEvent.ACTION_CANCEL. return false at the end is key so that any other listeners such as onClickListener will be called normally. – Doug Voss Apr 17 '17 at 17:38
  • @Finnboy11 you need to handle ACTION_CANCEL as well, it will be called inside scrollview. The details could be found here https://github.com/thanhbinh84/clickable-image-view/blob/master/app/src/main/java/com/thanhbinh84/clickableimageviewsample/EffectTouchListener.java – thanhbinh84 May 30 '17 at 12:13
  • @Bahu - it might be because you're implementing this method inside an OnClick so it runs only after the OnClick method is run. You should put it in an outer scope like right after you get a reference to the button you want to enable this effect on. – Ko Ga Nov 09 '17 at 16:39
  • How can I use both onTocuhListener and OnClickListener at the same time for single button/imageButton? – tpbafk Oct 16 '18 at 09:30
  • Best one out there – XCarb Mar 30 '20 at 15:36
93

Create your AlphaAnimation Object that decides how much will be the fading effect of the button, then let it start in the onClickListener of your buttons

For example :

private AlphaAnimation buttonClick = new AlphaAnimation(1F, 0.8F);

// some code

public void onClick(View v) {
    v.startAnimation(buttonClick);
}

of course this is just a way, not the most preferred one, it's just easier

sfmirtalebi
  • 370
  • 7
  • 16
Ahmed Adel Ismail
  • 2,168
  • 16
  • 23
93

You can simply use foreground for your View to achieve clickable effect:

android:foreground="?android:attr/selectableItemBackground"


For use with dark theme add also theme to your layout (to clickable effect be clear):

android:theme="@android:style/ThemeOverlay.Material.Dark"
Francis
  • 6,788
  • 5
  • 47
  • 64
  • 5
    The easiest way to implement. FYI, Attribute android:foreground has no effect on API levels lower than 23. – dolgom May 07 '19 at 18:44
  • 1
    Simplest solution. – Jerry Chong May 27 '19 at 01:43
  • 2
    This should be the accepted answer, much simpler and it adds the nice ripple animation effect. – Tyler Nov 13 '19 at 20:24
  • 1
    @dolgom For API level < 23 use it on "background" attribute.So: android:background="?android:attr/selectableItemBackground" – Z3R0 Mar 23 '20 at 08:40
  • @Z3R0 `android:background="@drawable/bluebutton"` is already taken to set the image for the button. It can't be used twice. Hence older Android versions are out of luck. Only the accepted solution works, if the button has no transparency. – Houman Oct 03 '20 at 20:41
  • @Houman you are right, this will work only if you don't need to set up the background. Anyway actually I'm using Butterknife and @OnClick(R.id.id_bttn) to set up onCLick Listener, check it if you need in java (Butterknife binds your view, in kotlin you have native binding) – Z3R0 Oct 06 '20 at 07:05
  • @Houman if the trasparency means the shape of the button, you can use ripple drawable (with the shape mask applied) as a `android:foreground`. – Francis Oct 06 '20 at 09:49
  • Simplest and Straight Forward. – MoonLight Sep 02 '21 at 14:43
27

To make your item consistent with the system look and feel try referencing the system attribute android:attr/selectableItemBackground in your desired view's background or foreground tag:

<ImageView
    ...
    android:background="?android:attr/selectableItemBackground"      
    android:foreground="?android:attr/selectableItemBackground"
    ...
/>

Use both attributes to get desired effect before/after API level 23 respectively.

https://stackoverflow.com/a/11513474/4683601

Ely Dantas
  • 705
  • 11
  • 23
11

Or using only one background image you can achive the click effect by using setOnTouchListener

Two ways

((Button)findViewById(R.id.testBth)).setOnTouchListener(new OnTouchListener() {

      @Override
        public boolean onTouch(View v, MotionEvent event) {
          switch (event.getAction()) {
              case MotionEvent.ACTION_DOWN: {
                  Button view = (Button) v;
                  view.getBackground().setColorFilter(0x77000000, PorterDuff.Mode.SRC_ATOP);
                  v.invalidate();
                  break;
              }
              case MotionEvent.ACTION_UP:
                  // Your action here on button click
              case MotionEvent.ACTION_CANCEL: {
                  Button view = (Button) v;
                  view.getBackground().clearColorFilter();
                  view.invalidate();
                  break;
              }
          }
          return true;
        }
});

enter image description here

And if you don't want to use setOnTouchLister, the another way of achieving this is

    myButton.getBackground().setColorFilter(.setColorFilter(0xF00, Mode.MULTIPLY);

    StateListDrawable listDrawable = new StateListDrawable();
    listDrawable.addState(new int[] {android.R.attr.state_pressed}, drawablePressed);
    listDrawable.addState(new int[] {android.R.attr.defaultValue}, myButton);

    myButton.setBackgroundDrawable(listDrawable);
Vinayak Bevinakatti
  • 40,205
  • 25
  • 108
  • 139
  • there are errors in code in the 2nd way. What is the getCurrent() function? Also myButton is not a Drawable so it can not be added as state. – Ehtesham Hasan Sep 10 '16 at 19:52
  • Thanks for pointing out the error. I have corrected it, and myButton is an instance of Button class which supplies the method setBackgroundDrawable and it takes the Drawable instances, hence this will work. – Vinayak Bevinakatti Sep 12 '16 at 14:25
  • Check out my answer which is based on your 2nd approach. http://stackoverflow.com/a/39430738/5229515 – Ehtesham Hasan Sep 12 '16 at 18:18
  • This should probably have "return false" at the end so it doesn't consume the event and any additional onClickListeners can be called. – Doug Voss Apr 17 '17 at 17:47
11

For all the views

android:background="?android:attr/selectableItemBackground"

But for cardview which has elevation use

android:foreground="?android:attr/selectableItemBackground"

For Circular click effect as in toolbar

android:background="?android:attr/actionBarItemBackground"

Also you need to set

 android:clickable="true"
 android:focusable="true"
Faizan Haidar Khan
  • 1,099
  • 1
  • 15
  • 20
  • I don't understand what you mean. `android:background="@drawable/bluebutton"` is used to set the background of the Button to an image. It is already taken. This can't work. – Houman Oct 03 '20 at 20:36
  • @Houman You can use ripple for this https://stackoverflow.com/a/40008782/8663316 – Faizan Haidar Khan Aug 02 '21 at 07:21
4

This is the best solution I came up with taking hints from @Vinayak's answer. All the other solutions have different drawbacks.

First of all create a function like this.

void addClickEffect(View view)
{
    Drawable drawableNormal = view.getBackground();

    Drawable drawablePressed = view.getBackground().getConstantState().newDrawable();
    drawablePressed.mutate();
    drawablePressed.setColorFilter(Color.argb(50, 0, 0, 0), PorterDuff.Mode.SRC_ATOP);

    StateListDrawable listDrawable = new StateListDrawable();
    listDrawable.addState(new int[] {android.R.attr.state_pressed}, drawablePressed);
    listDrawable.addState(new int[] {}, drawableNormal);
    view.setBackground(listDrawable);
}

Explanation:

getConstantState().newDrawable() is used to clone the existing Drawable otherwise the same drawable will be used. Read more from here: Android: Cloning a drawable in order to make a StateListDrawable with filters

mutate() is used to make the Drawable clone not share its state with other instances of Drawable. Read more about it here: https://developer.android.com/reference/android/graphics/drawable/Drawable.html#mutate()

Usage:

You can pass any type of View (Button, ImageButton, View etc) as the parameter to the function and they will get the click effect applied to them.

addClickEffect(myButton);
addClickEffect(myImageButton);
Community
  • 1
  • 1
Ehtesham Hasan
  • 4,133
  • 2
  • 23
  • 31
4

I had the same issue, where I needed to have a transparent background but still get animation. Setting this solved it for me:

android:background="?android:selectableItemBackground"

Also this if you want to have circular effect:

android:background="?android:selectableItemBackgroundBorderless"
MandelMech
  • 467
  • 4
  • 6
3

just wanna add another easy way to do this: If your ImageButton remains its background and you don't set it to null, it will work like a normal button and will show the click animation while clicking exactly like other buttons.The way to hide the background while it is still there:

<ImageButton
    android:id="@+id/imageButton2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:paddingBottom="1dp"
    android:paddingLeft="1dp"
    android:paddingRight="1dp"
    android:paddingTop="1dp"
    android:src="@drawable/squareicon" />

The paddings won't let the background be visible and make the button act like other buttons.

arianoo
  • 657
  • 14
  • 22
3
Step: set a button in XML with onClick Action:

 <Button
android:id="@+id/btnEditUserInfo"
style="?android:borderlessButtonStyle"
android:layout_width="wrap_content"
android:layout_height="@dimen/txt_height"
android:layout_gravity="center"
android:background="@drawable/round_btn"
android:contentDescription="@string/image_view"
android:onClick="edit_user_info"
android:text="Edit"
android:textColor="#000"
android:textSize="@dimen/login_textSize" />

Step: on button clicked show animation point
//pgrm mark ---- ---- ----- ---- ---- ----- ---- ---- -----  ---- ---- -----

    public void edit_user_info(View view) {

        // show click effect on button pressed
        final AlphaAnimation buttonClick = new AlphaAnimation(1F, 0.8F);
        view.startAnimation(buttonClick);

        Intent intent = new Intent(getApplicationContext(),  EditUserInfo.class);
        startActivity(intent);

    }// end edit_user_info
Vinod Joshi
  • 7,696
  • 1
  • 50
  • 51
3

Simple and easy way to set View click effect.

Method

public AlphaAnimation clickAnimation() {
    return new AlphaAnimation(1F, 0.4F); // Change "0.4F" as per your recruitment.
}

Set in View

yourView.startAnimation(clickAnimation());
Kumar Santanu
  • 603
  • 1
  • 7
  • 14
2

Use RippleDrawable for Material Design state pressed/clicked effect.

In order to achieve this, create ripple item as an .xml under /drawable folder and use it in android:background for any views.

  • Effect for icon pressed/clicked, use circular ripple effect, for example:

    <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="@android:color/darker_gray" />

  • Effect for view clicked with rectangle boundary, we can add ripple over the existing drawable like bellow:

    <?xml version="1.0" encoding="utf-8"?> <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="#000000"> <item android:drawable="@drawable/your_background_drawable"/> </ripple>

giang nguyen
  • 393
  • 4
  • 12
2

Create bounce.xml file fo animation

LOCATION:

res->anim->bounce.xml

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

    <scale
        android:duration="100"
        android:fromXScale="0.9"
        android:fromYScale="0.9"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="1.0"
        android:toYScale="1.0" />

</set>

Add this line in onClick to initialize

  final Animation myAnim = AnimationUtils.loadAnimation(this,R.anim.bounce);
         button.startAnimation(myAnim);

You get the shrink effect in a button click.

Arpit Patel
  • 7,212
  • 5
  • 56
  • 67
0

Making a minor addition to Andràs answer:

You can use postDelayed to make the color filter last for a small period of time to make it more noticeable:

        @Override
        public boolean onTouch(final View v, MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN: {
                    v.getBackground().setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
                    v.invalidate();
                    break;
                }
                case MotionEvent.ACTION_UP: {
                    v.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            v.getBackground().clearColorFilter();
                            v.invalidate();
                        }
                    }, 100L);
                    break;
                }
            }
            return false;
        }

You can change the value of the delay 100L to suit your needs.

hiddeneyes02
  • 2,562
  • 1
  • 31
  • 58
0

If you're using xml background instead of IMG, just remove this :

<item>
    <bitmap android:src="@drawable/YOURIMAGE"/>
</item>

from the 1st answer that @Ljdawson gave us.

0

I tried something to set button click effect in android- 1.I created a animation resource file to use as button click effect. 2.Created a animation listener. 3.On animation end I can set the task or activity which I want to start on button click. 4.Then set animation on button.

<scale
    android:duration="90"
    android:fromYScale="0.9"
    android:fromXScale="0.9"
    android:toYScale="1.0"
    android:toXScale="1.0"
    android:pivotY="50%"
    android:pivotX="50%"/>


Animation animationButton = AnimationUtils.loadAnimation(MyActivity.this,R.anim.bounce_button);

 animationButton.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
            }
            @Override
            public void onAnimationEnd(Animation animation) {
                Intent intent = new Intent(getApplicationContext(),SecondActivity.class);
                startActivity(intent);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                button.startAnimation(animationButton);


            }
        });

'''
I hope this will help.