J’essaie de comprendre comment mettre en œuvre “l’effet d’entraînement – Effet tactile” pour les boutons et autres vues. J’ai regardé les questions liées à l’effet tactile Ripple sur SO et j’ai eu un aperçu. J’ai pu obtenir l’effet d’entraînement en utilisant ce code 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.AtsortingbuteSet; 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 AtsortingbuteSet attrs) { super(context, attrs); init(); } public MyButton(final Context context, final AtsortingbuteSet 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); } }
Mais, je veux utiliser l’approche XML. Comment puis-je y arriver? J’ai regardé ceci et cela , mais je ne suis pas encore à l’aise avec les styles, donc je trouve difficile d’obtenir l’effet d’entraînement.
J’ai un bouton avec le code XML suivant:
Comment puis-je obtenir un effet d’entraînement pour ce bouton? Si quelqu’un peut me guider, je serai reconnaissant.
[EDIT] Ajouter ripple.xml et background.xml, comme mentionné dans l’un des liens ci-dessus. J’ai créé un dossier drawable-v21 dans res et y ai ajouté les fichiers ci-dessous.
ripple.xml
background.xml
J’ai ajouté l’ondulation comme arrière-plan pour mon bouton, voici le xml pour mon bouton maintenant ..
Lorsque je lance l’application, je reçois une exception ResourceNotFoundException. Voici la trace du 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 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
Vous pouvez faire quelque chose comme ça:
Où le ripple.xml est:
-
Il suffit de mettre ?attr/selectableItemBackground
en arrière-plan du bouton de l’API 21+, comme ci-dessous:
Pour lollipop (API> 21), faites un fichier comme btn_ripple_effect.xml dans drawable et mettez le code ci-dessous
-
Pour pre lollipop (API <21), créez le fichier comme btn_ripple_effect.xml dans le dossier drawable-v21 et placez le code ci-dessous
-
-
Légère addition à la réponse ci-dessus: Notez que la couleur du masque n’est utilisée d’aucune façon.
Vous pouvez aussi faire des choses plus compliquées avec des ondulations. Par exemple, si vous souhaitez une bordure sur votre bouton d’ondulation, vous pouvez l’utiliser comme une liste de calques.
-
-
Notez que l’élément avec id @android:id/mask
n’est utilisé que pour montrer où l’effet d’ondulation va s’arrêter. Si vous voulez qu’il couvre le bouton entier, vous pouvez changer l’ android:shape
à rectangle
. Vous pouvez imaginer faire beaucoup plus de choses intéressantes avec cela aussi!
Assurez-vous également de pouvoir utiliser une sauvegarde pour les périphériques qui ne sont pas encore 21, ou l’application se bloquera sur les anciens périphériques.
La meilleure façon de l’utiliser dans Android: le premier plan , car il vous permet également d’utiliser votre propre arrière-plan.
android: foreground = “? android: attr / selectableItemBackground”
Exemple:
Je cherchais un effet d’entraînement car c’était quelque chose que je voulais appliquer à quelques boutons de mon application et qui s’est passé dans votre message. Pendant que votre question cherche une réponse à la question de savoir comment append l’effet d’ondulation à l’aide de XML, c’est en fait quelque chose que j’essayais d’éviter car lorsque vous essayez d’append cet atsortingbut, vous devez le voir v21.
Si vous ciblez moins de v21 que cette nouvelle classe, l’extension Button (ou ImageButton, etc.) évitera les plaintes du compilateur.
Comme il n’y avait aucune explication sur la façon d’implémenter la classe personnalisée ci-dessus, j’ai pensé que je devais remplir.
De:
À:
C’est tout. Maintenant, lorsque vous appuyez sur ce bouton, une ondulation est contenue dans ses limites.
J’aime cette approche, je souhaite juste que l’ondulation dépasse les limites. Pour un petit bouton, cet effet d’entraînement montre à quel point le bouton est carré ou rectangular. Visuellement, cela serait plus satisfaisant si l’ondulation venait juste de continuer jusqu’à atteindre son plein rayon.
Vous pouvez append ?attr/selectableItemBackground
atsortingbuts clickable
tels que true
et background
ou foreround
?attr/selectableItemBackground
foreround
/ foreround
à la vue:
Si, dans le cas où votre vue contient déjà un background
– background
, vous pouvez remplir votre foreground
avec selectableItemBackground