Android L's Ripple Effect – Touch Feedback para los botones – Uso de XML

Estoy tratando de entender cómo implementar el efecto "Ripple Effect – Touch Feedback" para los botones y otras vistas. Miré las preguntas relacionadas con el efecto de toque de Ripple en SO y conseguí una cierta penetración en él. Fui capaz de obtener con éxito el efecto de rizo con este código java.

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); } } 

Pero, quiero usar el enfoque XML. ¿Cómo logro esto? He mirado esto y esto , pero todavía no estoy tan cómodo con los estilos, por lo que estoy encontrando difícil lograr el efecto de rizo.

Tengo un botón con el siguiente código XML:

  <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" /> 

¿Cómo obtengo efecto de rizado para este botón. Si alguien me puede guiar, estaré agradecido.

[EDIT] Añadir ripple.xml y background.xml, como se menciona en uno de los enlaces anteriores. He creado una carpeta drawable-v21 en res y agregó los archivos abajo allí.

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> 

He añadido la ondulación como fondo para mi botón, aquí está el xml para mi botón ahora ..

 <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" /> 

Cuando ejecuto la aplicación obtengo una ResourceNotFoundException. Aquí está el rastro logcat ..

 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 

Puedes hacer algo como esto:

 <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/ripple" /> 

Donde el ripple.xml es:

 <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> 

Más información aquí .

Sólo tiene que poner ?attr/selectableItemBackground en el fondo del botón para API 21+, como a continuación:

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

Para el lollipop (API> 21) haga el archivo como btn_ripple_effect.xml en drawable y ponga debajo del código

 <?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> 

Para pre lollipop (API <21), haga el archivo como btn_ripple_effect.xml en la carpeta drawable-v21 y coloque debajo del código

 <?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> 

Adición ligera a la respuesta anterior: Tenga en cuenta que el color de la máscara no se utiliza de ninguna manera.

Usted puede hacer cosas más complicadas con ondulación también. Por ejemplo, si desea un borde en el botón de ondulación, puede utilizarlo como una lista de capas.

 <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> 

Tenga en cuenta que el elemento con id @android:id/mask sólo se utiliza para mostrar dónde se detendrá el efecto ripple. Si querías que cubriera todo el botón, podrías cambiar el android:shape sería rectangle . Usted puede imaginar haciendo muchas cosas más interesantes con esto también!

También asegúrese de tener una copia de seguridad para dispositivos que aún no tienen 21 o la aplicación se bloqueará en dispositivos antiguos.

La mejor manera de usar esto en android: primer plano , ya que le permite utilizar su propio fondo también.

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

Ejemplo:

 <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"/> 

Yo estaba investigando efecto de rizo, ya que era algo que quería aplicar a algunos botones en mi aplicación y sucedió a través de su puesto. Mientras que su pregunta está buscando una respuesta a cómo agregar el efecto de la ondulación usando XML que era realmente algo que intentaba evitar mientras que al intentar agregar ese atributo usted ve que requiere v21.

Si está apuntando a menos de v21 que la nueva clase extendiendo el botón (o ImageButton, etc.) evitará quejas del compilador.

Como no había ninguna explicación sobre cómo implementar la clase personalizada anterior, pensé que podría rellenar. Todo lo que necesitas hacer es crear la nueva clase y luego en el cambio XML "Button" a "the.package.name.MyButton".

De:

  <Button android:id="@+id/Button" android:layout_width="wrap_content" android:layout_height="wrap_content" /> 

A:

  <the.package.name.MyButton android:id="@+id/Button" android:layout_width="wrap_content" android:layout_height="wrap_content" /> 

Eso es. Ahora su botón cuando está presionado tendrá una ondulación contenida dentro de sus límites.

Me gusta este enfoque Sólo deseo que la ondulación se extendería pasar los límites. Para un pequeño botón este efecto de rizo realmente destaca cuán cuadrado o rectangular es realmente el botón. Visualmente sería más satisfactorio que la ondulación continuara hasta que alcanzara su radio completo.

Puede agregar clickable como true y background o foreround como ?attr/selectableItemBackground foreround atributos a la vista:

 <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"/> 

Si en caso de que su vista ya tiene un background lleno de algo, podría llenar su foreground con 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"/> 
  • ¿Cómo hacer un botón que muestra el carácter de retroceso (⌫) en Android?
  • Recorte de visualización de Android
  • Eclipse ClassCastException al intentar expandir el diseño XML para Android
  • Actividad de configuración botón Arriba
  • Recurso duplicado del error de Android Studio
  • Tag mismatch error-Android studio
  • Espacio en blanco al escalar imageview en gridview
  • Ejemplo simple de uso de <merge> y <include> en diseños XML de Android
  • Android: El editor XML predeterminado ya no se abre
  • Mapa de bits de StateListDrawable y mosaico
  • Cómo ver AndroidManifest.xml de archivo APK?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.