151

I created a button and I want to add ripple effect to that button!

I created a button bg XML file: (bg_btn.xml)

<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<gradient android:startColor="#FFFFFF" android:endColor="#00FF00" android:angle="270" />
<corners android:radius="3dp" />
<stroke android:width="5px" android:color="#000000" />
</shape>

And this is my ripple effect file: (ripple_bg.xml)

<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:color="#f816a463"
    tools:targetApi="lollipop">
    <item android:id="@android:id/mask">
        <shape android:shape="rectangle">
            <solid android:color="#f816a463" />
        </shape>
    </item>
</ripple>

And This is my Button which I want to add ripple effect:

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="New Button"
android:id="@+id/button"
android:layout_centerHorizontal="true"
android:layout_marginTop="173dp"
android:textColor="#fff"
android:background="@drawable/ripple_bg"
android:clickable="true" />

But after adding ripple effect button background is transparent, and button display only when clicked, like this:

Before Click

On Click

But I need both button background color and ripple effect, I found some of this code in different blogs of Stack Overflow, but still it is not working!

Suragch
  • 364,799
  • 232
  • 1,155
  • 1,198
rakcode
  • 1,992
  • 2
  • 14
  • 35

14 Answers14

195

Here is another drawable xml for those who want to add all together gradient background, corner radius and ripple effect:

<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="@color/colorPrimaryDark">
    <item android:id="@android:id/mask">
        <shape android:shape="rectangle">
            <solid android:color="@color/colorPrimaryDark" />
            <corners android:radius="@dimen/button_radius_large" />
        </shape>
    </item>

    <item android:id="@android:id/background">
        <shape android:shape="rectangle">
            <gradient
                android:angle="90"
                android:endColor="@color/colorPrimaryLight"
                android:startColor="@color/colorPrimary"
                android:type="linear" />
            <corners android:radius="@dimen/button_radius_large" />
        </shape>
    </item>
</ripple>

Add this to the background of your button.

<Button
    ...
    android:background="@drawable/button_background" />

PS: this answer works for android api 21 and above.

Emi Raz
  • 1,079
  • 1
  • 13
  • 21
Pei
  • 10,336
  • 4
  • 35
  • 38
  • 5
    @pei How you guys get this infos from? I mean how you know that there's an XML element called ripple to do this job? –  Jun 14 '18 at 10:21
  • 1
    It's one of the concepts used in material design. Please start typing a new root tag in a drawable xml in Android Studio then you should be able to see all the available tags including `ripple`. – Pei Jun 14 '18 at 15:32
  • 1
    what is the use of mask item? – Prashant Aug 16 '18 at 05:07
  • It's the mask in which ripple effect is applied. Without it you won't be able to make the ripple effect to be in the same shape of the background. – Pei Aug 16 '18 at 05:14
  • 11
    this requires api level 21 – M.kazem Akhgary Oct 16 '18 at 20:04
  • 2
    it works but how it works is not explained. it should be related with item's id. Should be referenced. – oiyio Mar 21 '19 at 06:31
  • @Pei, where do you place this file? in the anim folder? – juztcode Jan 30 '20 at 06:23
  • @juztcode As you can see in the statement `android:background="@drawable/button_background"`, you should place this in the drawable folder. – Kraigolas Oct 02 '20 at 13:10
181

Add the "?attr/selectableItemBackground" to your view's android:foreground attribute if it already has a background along with android:clickable="true".

Vadim Kotov
  • 7,103
  • 8
  • 44
  • 57
backslashN
  • 2,363
  • 3
  • 12
  • 21
  • doesn't always work, but you can add a background to the ripple effect as noted in the comments of the accepted answer – Tyler Jan 03 '18 at 14:16
  • I had to use this solution as I have used a custom background for my button. It worked great. – bnayagrawal Sep 14 '18 at 06:01
  • @ROAR, that's right, but at least: "This is not an error; the application will simply ignore the attribute. However, if the attribute is important to the appearance or functionality of your application, you should consider finding an alternative way to achieve the same result with only available attributes, and then you can optionally create a copy of the layout in a layout-vNN folder which will be used on API NN or higher where you can take advantage of the newer attribute." So it works well on devices above API 23 and is ignored when below (we can just `tools:ignore="UnusedAttribute`). – JorgeAmVF Apr 17 '19 at 20:27
  • without Android support library, it would be `?android:attr/selectableItemBackground` (`android:attr` instead of `attr`) – Weekend Jun 17 '19 at 10:27
  • 8
    `android:foreground` attribute has not effect on API levels lower than 23 – Vadim Kotov Jan 31 '20 at 08:57
74

Add Ripple Effect/Animation to a Android Button

Just replace your button background attribute with android:background="?attr/selectableItemBackground" and your code looks like this.

      <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?attr/selectableItemBackground"
        android:text="New Button" />

Another Way to Add Ripple Effect/Animation to an Android Button

Using this method, you can customize ripple effect color. First, you have to create a xml file in your drawable resource directory. Create a ripple_effect.xml file and add following code. res/drawable/ripple_effect.xml

<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:color="#f816a463"
    tools:targetApi="lollipop">
    <item android:id="@android:id/mask">
        <shape android:shape="rectangle">
            <solid android:color="#f816a463" />
        </shape>
    </item>
</ripple>

And set background of button to above drawable resource file

<Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/ripple_effect"
    android:padding="16dp"
    android:text="New Button" />
Jigar Patel
  • 1,394
  • 2
  • 10
  • 26
  • 1
    For me, ripple effect did not appear. it was just a color switch for the button :/ My android version is 5.1.1 so it should work. Could you help? I followed your steps – Urvika G Jul 05 '17 at 11:00
  • android:color should be different from the mast android:color, or you want see the ripple effect – SpyZip Sep 07 '17 at 08:27
  • 3
    if you want a background you just add another item to the ripple, like: – Tyler Jan 03 '18 at 14:14
39

In addition to Jigar Patel's solution, add this to the ripple.xml to avoid transparent background of buttons.

<item
    android:id="@android:id/background"
    android:drawable="@color/your-color" />

Complete xml :

<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:color="@color/your-color"
    tools:targetApi="lollipop">
    <item android:id="@android:id/mask">
        <shape android:shape="rectangle">
            <solid android:color="@color/your-color" />
        </shape>
    </item>
    <item
        android:id="@android:id/background"
        android:drawable="@color/your-color" />
</ripple>

Use this ripple.xml as background in your button :

android:background="@drawable/ripple"
Sudheesh R
  • 1,624
  • 2
  • 17
  • 39
  • 3
    We didn't need to use the ` [...]`. Unless you want an oval mask. Thanks for your answer! – Filipe Brito Nov 01 '17 at 18:25
  • What does this do anyway, I created 2 similar elements with and without that attribute, and they look exactly the same – Sartheris Stormhammer Nov 24 '17 at 12:42
  • @SartherisStormhammer from official docs: If a mask layer is set, the ripple effect will be masked against that layer before it is drawn over the composite of the remaining child layers. If no mask layer is set, the ripple effect is masked against the composite of the child layers. This is the link, https://developer.android.com/reference/android/graphics/drawable/RippleDrawable.html – Sudheesh R Nov 24 '17 at 15:07
  • @SudheeshR that just makes it more confusing – Sartheris Stormhammer Nov 24 '17 at 19:22
  • @SartherisStormhammer From that official documentation It's pretty clear I think. Ripple effect will draw over child/masked layers. Please read the doc again, you'll get the point. – Sudheesh R Nov 25 '17 at 08:46
  • You need to use different colors for ripple and for background, but not the same `"@color/your-color"` – fdermishin Apr 06 '18 at 07:59
  • Absolutely. I didn't meant those are same colors.. Just use whatever color you want.. – Sudheesh R Apr 06 '18 at 15:17
35

When the button has a background from the drawable, we can add ripple effect to the foreground parameter.. Check below code its working for my button with a different background

    <Button
    android:layout_width="wrap_content"
    android:layout_height="40dp"
    android:gravity="center"
    android:layout_centerHorizontal="true"                                                             
    android:background="@drawable/shape_login_button"
    android:foreground="?attr/selectableItemBackgroundBorderless"
    android:clickable="true"
    android:text="@string/action_button_login"
     />

Add below parameter for the ripple effect

   android:foreground="?attr/selectableItemBackgroundBorderless"
   android:clickable="true"

For reference refer below link https://jascode.wordpress.com/2017/11/11/how-to-add-ripple-effect-to-an-android-app/

Jasmine John
  • 773
  • 7
  • 11
  • 3
    Attribute android:foreground has no effect on API levels lower than 23 – Ali Khaki Jun 06 '19 at 15:32
  • 1
    One drawback of this approach is, even though my button background was oval the ripple propagated till the rectangular edges. – iCantC Mar 17 '20 at 07:29
20

AppCompat v7+

If you don't prefix with ?android: your app will crash.

You should use "?android:attr/selectableItemBackground" or "?android:attr/selectableItemBackgroundBorderless", based on your preference. I prefer Borderless.

You can put it either in android:background or android:foreground to keep existing properties.

The element must have android:clickable="true" and android:focusable="true" in order for this to work, but many elements, such as buttons, have them true by default.

<Button
    ...
    android:background="@color/white"
    android:foreground="?android:attr/selectableItemBackgroundBorderless"
/>
<TextView
    ...
    android:background="?android:attr/selectableItemBackgroundBorderless"
    android:clickable="true"
    android:focusable="true"
/>

Programmatically (Java)

TypedValue value = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.selectableItemBackground, value, true);
myView.setBackgroundResource(value.resourceId);
myView.setFocusable(true); // If needed for view type

Programmatically (Kotlin)

val value = TypedValue()
context.theme.resolveAttribute(android.R.attr.selectableItemBackground, value, true)
myView.setBackgroundResource(value.resourceId)
myView.setFocusable(true) // If needed for view type

Reusable Kotlin Extension Function

myView.ripple()

fun View.ripple(): View {
    val value = TypedValue()
    context.theme.resolveAttribute(android.R.attr.selectableItemBackground, value, true)
    setBackgroundResource(value.resourceId)
    isFocusable = true // Required for some view types
    return this
}
Gibolt
  • 24,018
  • 9
  • 129
  • 89
  • If you add `?android` you won't be using the AppCompat lib, but the one from the OS (so it will crash in api<11). You have to use it without the `android:` namespace prefix. – David Miguel Sep 08 '20 at 20:46
  • Thanks for the note . I'd expect most devs have min level above 11, so no problem – Gibolt Sep 08 '20 at 21:09
7

In addition to Sudheesh R

Add Ripple Effect/Animation to a Android Button with button rectangle shape with corner

Create xml file res/drawable/your_file_name.xml

<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:color="@color/colorWhite"
    tools:targetApi="lollipop">
    <item android:id="@android:id/mask">
        <shape android:shape="rectangle">
            <solid android:color="@color/colorPrimaryDark" />
            <corners android:radius="50dp" />
        </shape>
    </item>

    <item android:id="@android:id/background">
        <shape android:shape="rectangle">
            <gradient
                android:angle="90"
                android:endColor="@color/colorAccent"
                android:startColor="@color/colorPrimary"
                android:type="linear" />
            <corners android:radius="50dp" />
        </shape>
    </item>
</ripple>
Digvijay Machale
  • 581
  • 6
  • 12
7

Adding foreground and clickable attributes worked for me.

<Button
    ... 
    android:background="@color/your_color"
    android:foreground="?attr/selectableItemBackgroundBorderless"
    android:clickable="true" />
Ashwin
  • 4,152
  • 33
  • 42
7

A simple approach is to set a view theme as outlined here.

some_view.xml

<ImageView
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:background="?attr/selectableItemBackgroundBorderless"
   android:focusable="true"
   android:src="@drawable/up_arrow"
   android:theme="@style/SomeButtonTheme"/>

some_style.xml

<style name="SomeButtonTheme" >
   <item name="colorControlHighlight">@color/someColor</item>
</style>
Community
  • 1
  • 1
Adam Hurwitz
  • 6,459
  • 4
  • 48
  • 92
4

When you use android:background, you are replacing much of the styling and look and feel of a button with a blank color.

Update: As of the version 23.0.0 release of AppCompat, there is a new Widget.AppCompat.Button.A colored style which uses your theme's colorButtonNormal for the disabled color and colorAccent for the enabled color.

This allows you apply it to your button directly via

<Button
 ...
style="@style/Widget.AppCompat.Button.Colored" />

You can use a drawable in your v21 directory for your background such as:

<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="?attr/colorControlHighlight">
<item android:drawable="?attr/colorPrimary"/>
</ripple>

This will ensure your background color is ?attr/colorPrimary and has the default ripple animation using the default ?attr/colorControlHighlight (which you can also set in your theme if you'd like).

Note: you'll have to create a custom selector for less than v21:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/primaryPressed"            android:state_pressed="true"/>
<item android:drawable="@color/primaryFocused" android:state_focused="true"/>
<item android:drawable="@color/primary"/>
</selector>
Atman Bhatt
  • 1,107
  • 7
  • 12
4

try this

<Button
            android:id="@+id/btn_location"
            android:layout_width="121dp"
            android:layout_height="38dp"
            android:layout_gravity="center"
            android:layout_marginBottom="24dp"
            android:layout_marginTop="24dp"
            android:foreground="?attr/selectableItemBackgroundBorderless"
            android:clickable="true"
            android:background="@drawable/btn_corner"
            android:gravity="center_vertical|center_horizontal"
            android:paddingLeft="13dp"
            android:paddingRight="13dp"
            android:text="Save"
            android:textColor="@color/color_white" />
Raveendra
  • 71
  • 6
3

Just use :

android:backgroundTint="#f816a463"

Instead of:

android:background="#f816a463"

Don't forget to change your Button to android.support.v7.widget.AppCompatButton

Gilad Shapira
  • 185
  • 2
  • 6
1

An alternative solution to using the <ripple> tag (which I personally prefer not to do, because the colors are not "default"), is the following:

Create a drawable for the button background, and include <item android:drawable="?attr/selectableItemBackground"> in the <layer-list>

Then (and I think this is the important part) programmatically set backgroundResource(R.drawable.custom_button) on your button instance.

Activity/Fragment

Button btn_temp = view.findViewById(R.id.btn_temp);
btn_temp.setBackgroundResource(R.drawable.custom_button);

Layout

<Button
    android:id="@+id/btn_temp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/custom_button"
    android:text="Test" />

custom_button.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape android:shape="rectangle">
            <solid android:color="@android:color/white" />
            <corners android:radius="10dp" />
        </shape>
    </item>
    <item android:drawable="?attr/selectableItemBackground" />
</layer-list>
Aidan Host
  • 136
  • 2
  • 11
0

setForeground is added in API level 23. Leverage the power of RevealAnimator in case u need to relay on foreground property !

 <View
   android:id="@+id/circular_reveal"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:background="@color/primaryMilk_22"
   android:elevation="@dimen/margin_20"
   android:visibility="invisible" />

With kotlin ext function, it's way osm !

fun View.circularReveal() {
    val cx: Int = width / 2
    val cy: Int = height / 2
    val finalRadius: Int =
        width.coerceAtLeast(height)
    val anim: Animator = ViewAnimationUtils.createCircularReveal(
        this,
        cx,
        cy,
        0f,
        finalRadius.toFloat()
    )
    anim.interpolator = AccelerateDecelerateInterpolator()
    anim.duration = 400
    isVisible = true
    anim.start()
    anim.doOnEnd {
        isVisible = false
    }
}
Anoop M Maddasseri
  • 8,060
  • 3
  • 40
  • 61