J'essaie de comprendre comment implémenter l '"Effet d'ondulation - Retour au toucher" pour les boutons et autres vues. J'ai regardé les questions liées à l'effet tactile Ripple sur SO et y ai eu un aperçu. J'ai réussi à obtenir l'effet d'entraînement en utilisant ceci 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);
}
}
Mais, je veux utiliser l'approche XML. Comment puis-je y parvenir? J'ai jeté un œil à this et this , mais je ne suis pas encore à l'aise avec les styles et j'ai donc du mal à obtenir l'effet d'entraînement.
J'ai un bouton avec le code XML suivant:
<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" />
Comment puis-je obtenir un effet d'entraînement pour ce bouton. Si quelqu'un peut me guider, je serai reconnaissant.
[EDIT] Ajout de ripple.xml et background.xml, comme indiqué dans l'un des liens ci-dessus. J'ai créé un dossier drawable-v21 en résolution et ajouté les fichiers ci-dessous.
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>
J'ai ajouté l'ondulation comme fond pour mon bouton, voici le code xml pour mon bouton maintenant ..
<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" />
Lorsque je lance l'application, je reçois une exception ResourceNotFoundException. Voici la trace 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
Vous pouvez faire quelque chose comme ça:
<Button
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:background="@drawable/ripple"
/>
Où le ripple.xml est:
<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>
Il suffit de mettre ?attr/selectableItemBackground
en arrière-plan du bouton pour API 21+, comme ci-dessous:
<Button
Android:layout_width="match_parent"
Android:layout_height="70dp"
Android:background="?attr/selectableItemBackground"
Android:text="Button" />
Pour Lollipop (API> 21), créez le fichier btn_ripple_effect.xml dans drawable et mettez-le sous le 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>
Avant Lollipop (API <21), créez le fichier sous le nom btn_ripple_effect.xml dans le dossier drawable-v21 et placez-le sous le 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>
Légère addition à la réponse ci-dessus: Notez que la couleur du masque n'est en aucun cas utilisée.
Vous pouvez également faire des choses plus complexes avec ondulation. Par exemple, si vous voulez une bordure sur votre bouton d'ondulation, vous pouvez l'utiliser comme une liste de calques.
<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>
Notez que l'élément avec id @Android:id/mask
n'est utilisé que pour indiquer où s'arrête l'effet d'entraînement. Si vous voulez couvrir tout le bouton, vous pouvez changer le Android:shape
être rectangle
. Vous pouvez aussi imaginer faire beaucoup plus de choses intéressantes avec ça!
Assurez-vous également d'avoir une sauvegarde extractible pour les périphériques n'ayant pas encore 21 ans, sinon l'application va se bloquer sur les anciens périphériques.
La meilleure façon de l'utiliser dans Android: au premier plan, car cela vous permet également d'utiliser votre propre arrière-plan.
Android: foreground = "? Android: attr/selectableItemBackground"
Exemple:
<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"/>
Je faisais des recherches sur l'effet d'entraînement car je voulais appliquer certains boutons de mon application et cela s'est passé dans votre message. Tandis que votre question cherchait une réponse sur la manière d’ajouter l’effet d’ondulation à l’aide de XML, c’était quelque chose que j’essayais d’éviter, c’est-à-dire que, lorsque j'essaie d’ajouter cet attribut, il nécessite la version 21.
Si vous ciblez une version inférieure à v21 par rapport à cette nouvelle classe, l'extension de Button (ou ImageButton, etc.) évitera les plaintes du compilateur.
Comme il n'y avait aucune explication sur la manière d'implémenter la classe personnalisée ci-dessus, je pensais que je devrais remplir. Tout ce que vous avez à faire est de créer la nouvelle classe, puis dans le code XML, changez "Button" en "the.package.name.MyButton".
De:
<Button
Android:id="@+id/Button"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content" />
À:
<the.package.name.MyButton
Android:id="@+id/Button"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content" />
C'est ça. Maintenant, lorsque vous appuyez sur le bouton, une ondulation est contenue dans ses limites.
J'aime cette approche, je souhaite juste que l'ondulation s'étende au-delà des limites. Pour un petit bouton, cet effet d'entraînement met vraiment en évidence le fait que le bouton est vraiment carré ou rectangulaire. Visuellement, il serait plus satisfaisant que l’ondulation se poursuive jusqu’à atteindre son rayon complet.
Vous pouvez ajouter clickable
comme true
et background
ou foreround
comme ?attr/selectableItemBackground
attributs de la vue:
<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 votre vue contient déjà un background
rempli avec quelque chose, vous pouvez remplir votre foreground
avec 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"/>