web-dev-qa-db-fra.com

Android Ripple Effect de L - Commentaires au toucher pour les boutons - Utilisation de XML

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
49
Vamsi Challa

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>
70
Gabriele Mariotti

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" />
28
Pacific P. Regmi

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

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.

12
William

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"/>
7
Md Imran Choudhury

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.

3
Jason Marks

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