Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android L's Ripple Effect - Touch Feedback for Buttons - Using XML

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 and got some insight into it. I was able to successfully get the ripple effect using this java code.

import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.RadialGradient; import android.graphics.Region; import android.graphics.Shader; import android.support.annotation.NonNull; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.animation.AccelerateInterpolator; import android.widget.Button;  public class MyButton extends Button {      private float mDownX;     private float mDownY;      private float mRadius;      private Paint mPaint;      public MyButton(final Context context) {         super(context);         init();     }      public MyButton(final Context context, final AttributeSet attrs) {         super(context, attrs);         init();     }      public MyButton(final Context context, final AttributeSet attrs,             final int defStyle) {         super(context, attrs, defStyle);         init();     }      private void init() {         mPaint = new Paint();         mPaint.setAlpha(100);     }      @Override     public boolean onTouchEvent(@NonNull final MotionEvent event) {         if (event.getActionMasked() == MotionEvent.ACTION_UP) {             mDownX = event.getX();             mDownY = event.getY();              ObjectAnimator animator = ObjectAnimator.ofFloat(this, "radius", 0,                     getWidth() * 3.0f);             animator.setInterpolator(new AccelerateInterpolator());             animator.setDuration(400);             animator.start();         }         return super.onTouchEvent(event);     }      public void setRadius(final float radius) {         mRadius = radius;         if (mRadius > 0) {             RadialGradient radialGradient = new RadialGradient(mDownX, mDownY,                     mRadius * 3, Color.TRANSPARENT, Color.BLACK,                     Shader.TileMode.MIRROR);             mPaint.setShader(radialGradient);         }         invalidate();     }      private Path mPath = new Path();     private Path mPath2 = new Path();      @Override     protected void onDraw(@NonNull final Canvas canvas) {         super.onDraw(canvas);          mPath2.reset();         mPath2.addCircle(mDownX, mDownY, mRadius, Path.Direction.CW);          canvas.clipPath(mPath2);          mPath.reset();         mPath.addCircle(mDownX, mDownY, mRadius / 3, Path.Direction.CW);          canvas.clipPath(mPath, Region.Op.DIFFERENCE);          canvas.drawCircle(mDownX, mDownY, mRadius, mPaint);     } } 

But, i want to use XML approach. How do i achieve this? I have looked at this and this, but i am not yet that comfortable with styles, so i am finding it difficult to achieve the ripple effect.

I have a button with the following XML code:

 <Button             android:id="@+id/button_email"             android:layout_width="0dip"             android:layout_height="wrap_content"             android:layout_weight="0.50"             android:gravity="center"             android:text="@string/email" /> 

How do i get ripple effect for this button. If someone can guide me, I will be thankful.

[EDIT] Adding ripple.xml and background.xml, as mentioned in one of the links above. I have created a drawable-v21 folder in res and added the below files there.

ripple.xml

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

background.xml

<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" >     <solid android:color="@android:color/darker_gray" /> </shape> 

I added the ripple as background for my button, here is the xml for my button now..

<Button     android:id="@+id/button_email"     android:layout_width="0dip"     android:layout_height="wrap_content"     android:layout_weight="0.50"     android:gravity="center"     android:background="@drawable/ripple"     android:text="@string/email" /> 

When i run the application i get a ResourceNotFoundException. Here is the logcat trace..

07-21 17:03:39.043: E/AndroidRuntime(15710): FATAL EXCEPTION: main 07-21 17:03:39.043: E/AndroidRuntime(15710): Process: com.xx.xxx, PID: 15710 07-21 17:03:39.043: E/AndroidRuntime(15710): android.view.InflateException: Binary XML file line #60: Error inflating class <unknown> 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.view.LayoutInflater.createView(LayoutInflater.java:620) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:56) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.view.LayoutInflater.onCreateView(LayoutInflater.java:669) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:694) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.view.LayoutInflater.rInflate(LayoutInflater.java:755) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.view.LayoutInflater.rInflate(LayoutInflater.java:758) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.view.LayoutInflater.inflate(LayoutInflater.java:492) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.view.LayoutInflater.inflate(LayoutInflater.java:397) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at com.xx.xxx.BusinessAdapter.onCreateViewHolder(BusinessAdapter.java:106) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at com.xx.xxx.BusinessAdapter.onCreateViewHolder(BusinessAdapter.java:1) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.support.v7.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:2915) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:2511) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.support.v7.widget.LinearLayoutManager$RenderState.next(LinearLayoutManager.java:1425) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:999) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:524) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:1461) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:1600) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.view.View.layout(View.java:14817) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.view.ViewGroup.layout(ViewGroup.java:4631) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.view.View.layout(View.java:14817) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.view.ViewGroup.layout(ViewGroup.java:4631) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.view.View.layout(View.java:14817) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.view.ViewGroup.layout(ViewGroup.java:4631) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at com.android.internal.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:374) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.view.View.layout(View.java:14817) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.view.ViewGroup.layout(ViewGroup.java:4631) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.view.View.layout(View.java:14817) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.view.ViewGroup.layout(ViewGroup.java:4631) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:1983) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1740) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:996) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5600) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.view.Choreographer.doCallbacks(Choreographer.java:574) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.view.Choreographer.doFrame(Choreographer.java:544) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.os.Handler.handleCallback(Handler.java:733) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.os.Handler.dispatchMessage(Handler.java:95) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.os.Looper.loop(Looper.java:136) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.app.ActivityThread.main(ActivityThread.java:5001) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at java.lang.reflect.Method.invokeNative(Native Method) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at java.lang.reflect.Method.invoke(Method.java:515) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at dalvik.system.NativeStart.main(Native Method) 07-21 17:03:39.043: E/AndroidRuntime(15710): Caused by: java.lang.reflect.InvocationTargetException 07-21 17:03:39.043: E/AndroidRuntime(15710):    at java.lang.reflect.Constructor.constructNative(Native Method) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at java.lang.reflect.Constructor.newInstance(Constructor.java:423) 07-21 17:03:39.043: E/AndroidRuntime(15710):    at android.view.LayoutInflater.createView(LayoutInflater.java:594) 07-21 17:03:39.043: E/AndroidRuntime(15710):    ... 50 more 07-21 17:03:39.043: E/AndroidRuntime(15710): Caused by: android.content.res.Resources$NotFoundException: Resource is not a Drawable (color or path): TypedValue{t=0x1/d=0x7f020075 a=-1 r=0x 
like image 535
Vamsi Challa Avatar asked Jul 21 '14 10:07

Vamsi Challa


People also ask

What is Android ripple effect?

What is Ripple effect in Android? Ripple effect provides an instantaneous visual confirmation at the point of contact when users interact with UI elements. These UI elements could be any of the View elements. Like – Layouts, Buttons, TextViews, ListViews, etc.

What is touch ripple?

Overview. Ripple touch effect was introduced with material design in Android 5.0 (API level 21). Touch feedback in material design provides an instantaneous visual confirmation at the point of contact when users interact fwith UI elements.

What is ripple color Android?

As you've noticed, ripples are used subtle indications of touch feedback, hence why they do not use colorPrimary or colorAccent by default. This is consistent with the changes made in Android 4.4 (Kitkat) which made the default selector colors neutral by default.


2 Answers

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> 
like image 168
Gabriele Mariotti Avatar answered Sep 23 '22 18:09

Gabriele Mariotti


Just put ?attr/selectableItemBackground in the background of button for API 21+ , like below:

<Button         android:layout_width="match_parent"         android:layout_height="70dp"         android:background="?attr/selectableItemBackground"         android:text="Button" /> 
like image 42
Pacific P. Regmi Avatar answered Sep 26 '22 18:09

Pacific P. Regmi