I am trying to understand how to implement the \"Ripple Effect - Touch Feedback\" for buttons and other views. I looked at the questions related to Ripple touch effect on SO
Slight addition to above answer: Note that the mask color is not used in any way.
You can do more complicated things with ripple as well. For example, if you wanted a border on your ripple button you can use it like a layer-list.
<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:colorControlHighlight">
<!-- Note: <ripple> acts like a layer-list -->
<item android:id="@android:id/mask">
<shape android:shape="oval">
<!-- This color is not displayed in any way -->
<solid android:color="@android:color/black" />
</shape>
</item>
<!-- This is the border -->
<item>
<shape android:shape="rectangle">
<corners android:radius="3dp"/>
<!-- Use your border color in place of #f00 -->
<stroke android:width="1dp" android:color="#f00"/>
</shape>
</item>
</ripple>
Note that the element with id @android:id/mask
is only used to show where the ripple effect will stop at. If you wanted it to cover the whole button, you could change the android:shape
to be rectangle
. You can imagine doing many more interesting things with this as well!
Also make sure to have a backup drawable for devices that aren't 21 yet or the app will crash on old devices.
The best way to use this in android:foreground, because it allows you use own background also.
android:foreground="?android:attr/selectableItemBackground"
Example:
<android.support.v7.widget.AppCompatButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:foreground="?android:attr/selectableItemBackground"
android:background="@color/button.normal"
android:textColor="@color/white"/>
UPDATE Material Components:
With the Material Components Library it is very easy to apply a ripple.
Just use the MaterialButton and the app:rippleColor
attribute:
<com.google.android.material.button.MaterialButton
app:rippleColor="@color/my_selector"
../>
With a selector like this:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="..." android:color="?attr/colorOnPrimary" android:state_pressed="true"/>
<item android:alpha="..." android:color="?attr/colorOnPrimary" android:state_focused="true" android:state_hovered="true"/>
<item android:alpha="..." android:color="?attr/colorOnPrimary" android:state_focused="true"/>
<item android:alpha="..." android:color="?attr/colorOnPrimary" android:state_hovered="true"/>
<item android:alpha="..." android:color="?attr/colorOnPrimary"/>
</selector>
Old answer
You can do something like this:
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/ripple"
/>
Where the ripple.xml is:
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:colorControlHighlight">
<item android:id="@android:id/mask">
<shape android:shape="oval">
<solid android:color="?android:colorAccent" />
</shape>
</item>
</ripple>
You can add clickable
as true
and background
or foreround
as ?attr/selectableItemBackground
attributes to the view:
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button"
android:clickable="true"
android:background="?attr/selectableItemBackground"
android:textColor="@android:color/white"/>
If in case your view already has a background
filled with something, you could fill your foreground
with selectableItemBackground
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button"
android:clickable="true"
android:foreground="?attr/selectableItemBackground"
android:background="@color/colorPrimary"
android:textColor="@android:color/white"/>
For lollipop(API>21) make file as btn_ripple_effect.xml in drawable and put below code
<?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="?android:colorAccent"
tools:targetApi="lollipop">
<item android:drawable="@color/cancel_btn_clr" /> <!-- default -->
<item android:id="@android:id/mask">
<shape android:shape="rectangle">
<solid android:color="?android:colorAccent" />
</shape>
</item>
</ripple>
For pre lollipop (API<21)make file as btn_ripple_effect.xml in drawable-v21 folder and put below code
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape>
<solid android:color="@color/colorAccent"></solid>
</shape>
</item>
<item>
<shape>
<solid android:color="@color/cancel_btn_clr"></solid>
</shape>
</item>
</selector>
I was researching ripple effect as it was something I wanted to apply to a few buttons in my application and happened across your post. While your question is searching for an answer as to how to add the ripple effect using XML that was actually something I was trying to avoid as when trying to add that attribute you see it requires v21.
If you are targeting lower than v21 than that new class extending Button (or ImageButton, etc.) will avoid complaints from the compiler.
As there was no explanation on how to implement the custom class above I thought I would fill in. All you need to do is create the new class and then in the XML change "Button" to "the.package.name.MyButton".
From:
<Button
android:id="@+id/Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
To:
<the.package.name.MyButton
android:id="@+id/Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
That's it. Now your button when pressed will have a ripple contained within its bounds.
I like this approach I just wish the ripple would extend pass the bounds. For a small button this ripple effect really highlights how square or rectangular the button really is. Visually it would be more satisfying if the ripple just continued until it reached its full radius.