代码之家  ›  专栏  ›  技术社区  ›  Vamsi Challa

Android L的涟漪效果-按钮的触摸反馈-使用XML

  •  50
  • Vamsi Challa  · 技术社区  · 10 年前

    我试图了解如何为按钮和其他视图实现“涟漪效果-触摸反馈”。我查看了与SO上的涟漪触摸效果相关的问题,并对其进行了一些深入了解。

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

    但是,我想使用XML方法。我如何做到这一点?我看过 this this ,但我对风格还不太熟悉,所以我发现很难实现涟漪效应。

    我有一个带有以下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" />
    

    如何获得此按钮的涟漪效果。如果有人能指导我,我会感激的。

    [EDIT]添加ripple.xml和background.xml,如上面的链接之一所述。我在res中创建了一个drawinable-v21文件夹,并在其中添加了以下文件。

    波纹.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>
    

    背景.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>
    

    我为我的按钮添加了波纹作为背景,这里是我的按钮的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:background="@drawable/ripple"
        android:text="@string/email" />
    

    当我运行应用程序时,我得到一个ResourceNotFoundException。这是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
    
    7 回复  |  直到 7 年前
        1
  •  72
  •   Gabriele Mariotti    4 年前

    更新材料组件:

    使用 Material Components Library 很容易施加波纹。
    只需使用 MaterialButton 以及 app:rippleColor 属性:

    <com.google.android.material.button.MaterialButton
        app:rippleColor="@color/my_selector"
        ../>
    

    使用这样的选择器:

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

    旧答案
    你可以这样做:

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

    其中ripple.xml为:

    <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>
    
        2
  •  30
  •   Pacific P. Regmi    9 年前

    放好 ?attr/selectableItemBackground 在API 21+的按钮背景中,如下所示:

    <Button
            android:layout_width="match_parent"
            android:layout_height="70dp"
            android:background="?attr/selectableItemBackground"
            android:text="Button" />
    
        3
  •  20
  •   akohout alaeri    8 年前

    对于棒棒糖(API>21),将文件制作为可绘制的btn_ripple_effect.xml,并放入以下代码

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

    对于预棒棒糖(API<21),在drawbable-v21文件夹中创建btn_ripple_effect.xml文件,并放入以下代码

    <?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>
    
        4
  •  12
  •   William    9 年前

    对上述答案稍作补充:请注意,掩模颜色未以任何方式使用。

    你也可以用波纹做更复杂的事情。例如,如果您希望涟漪按钮上有边框,可以将其用作图层列表。

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

    注意,id为的元素 @android:id/mask 仅用于显示涟漪效果停止的位置。如果您希望它覆盖整个按钮,可以更改 android:shape 成为 rectangle 。你可以想象用这个做更多有趣的事情!

    此外,请确保为尚未21岁的设备提供备份,否则应用程序将在旧设备上崩溃。

        5
  •  7
  •   Md Imran Choudhury    7 年前

    最好的使用方法 android:前景 ,因为它还允许您使用自己的背景。

    android:foreport=“?android:attr/selectableItemBackground”

    例子:

    <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"/>
    
        6
  •  3
  •   Jason Marks    9 年前

    我在研究涟漪效应,因为这是我想应用到我的应用程序中的几个按钮上的东西,并发生在你的帖子中。当您的问题正在寻找如何使用XML添加涟漪效应的答案时,这实际上是我试图避免的,因为当尝试添加该属性时,您看到它需要v21。

    如果目标低于v21,则新的类扩展Button(或ImageButton等)将避免编译器的投诉。

    由于上面没有关于如何实现自定义类的说明,我想我会填写。您需要做的就是创建新类,然后在XML中将“Button”更改为“.package.name.MyButton”。

    发件人:

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

    就是这样。现在按下按钮时,其范围内会包含一个波纹。

    我喜欢这种方法,我只是希望波纹能够越过边界。对于一个小按钮来说,这种波纹效果确实突出了按钮的真正方形或矩形。从视觉上看,如果波纹一直持续到达到其整个半径,会更令人满意。

        7
  •  1
  •   backslashN    7 年前

    您可以添加 clickable true background foreround ?attr/selectableItemBackground 视图的属性:

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

    如果您的视图已经具有 出身背景 充满了一些东西,你可以 foreground 具有 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"/>