Android5.0以上的项目都会有的按钮点击特效--水波纹

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <shape xmlns:android="http://schemas.android.com/apk/res/android">  
  3.     <solid android:color="@color/colorPrimary"/>  
  4.     <corners android:radius="10dp" />  
  5.     <padding android:left="20dp" android:top="20dp"  
  6.         android:right="20dp" android:bottom="20dp" />  
  7. </shape>  
  8.  

    原创 2016年01月06日 14:09:40

    下面是点击的效果

    这里写图片描述

    话说这种效果应该怎样实现呢,目前我是专门针对5.0以上系统建立一个文件夹drawable-v21,里面放置带有水波纹特效的点击效果: 
    写一个ripple标签,这个就是水波纹特效

    <?xml version="1.0" encoding="utf-8"?>
    <ripple xmlns:android="http://schemas.android.com/apk/res/android"
        android:color="#FF9e9e9e">
        <item android:drawable="@drawable/bg_nomal"/>
    </ripple>
    • 1
    • 2
    • 3
    • 4
    • 5

    color是点击水波纹的颜色,一般推荐FF9e9e9e; 
    如果这个点击效果需要默认的图片,就是drawable的内容了,这时color的颜色最好是drawable中颜色的加深色;

    对于5.0以下的版本就是设置一个相同的名字的点击效果就OK了,这样就可以在android5.0以上的按钮上添加酷炫的水波纹点击效果了

    1. import android.content.Context;  
    2. import android.content.res.TypedArray;  
    3. import android.graphics.Bitmap;  
    4. import android.graphics.Canvas;  
    5. import android.graphics.Color;  
    6. import android.graphics.Paint;  
    7. import android.graphics.PorterDuff;  
    8. import android.graphics.PorterDuffXfermode;  
    9. import android.graphics.Rect;  
    10. import android.os.Build;  
    11. import android.os.Handler;  
    12. import android.util.AttributeSet;  
    13. import android.view.GestureDetector;  
    14. import android.view.MotionEvent;  
    15. import android.view.animation.Animation;  
    16. import android.view.animation.ScaleAnimation;  
    17. import android.widget.AdapterView;  
    18. import android.widget.RelativeLayout;  
    19.   
    20. /**  
    21.  * Created by Administrator on 2016/5/3.  
    22.  */  
    23. public class RippleView extends RelativeLayout{  
    24.   
    25.     private int WIDTH;  
    26.     private int HEIGHT;  
    27.     private int frameRate = 10;  
    28.     private int rippleDuration = 400;  
    29.     private int rippleAlpha = 90;  
    30.     private Handler canvasHandler;  
    31.     private float radiusMax = 0;  
    32.     private boolean animationRunning = false;  
    33.     private int timer = 0;  
    34.     private int timerEmpty = 0;  
    35.     private int durationEmpty = -1;  
    36.     private float x = -1;  
    37.     private float y = -1;  
    38.     private int zoomDuration;  
    39.     private float zoomScale;  
    40.     private ScaleAnimation scaleAnimation;  
    41.     private Boolean hasToZoom;  
    42.     private Boolean isCentered;  
    43.     private Integer rippleType;  
    44.     private Paint paint;  
    45.     private Bitmap originBitmap;  
    46.     private int rippleColor;  
    47.     private int ripplePadding;  
    48.     private GestureDetector gestureDetector;  
    49.     private final Runnable runnable = new Runnable() {  
    50.         @Override  
    51.         public void run() {  
    52.             invalidate();  
    53.         }  
    54.     };  
    55.   
    56.     private OnRippleCompleteListener onCompletionListener;  
    57.   
    58.     public RippleView(Context context) {  
    59.         super(context);  
    60.     }  
    61.   
    62.     public RippleView(Context context, AttributeSet attrs) {  
    63.         super(context, attrs);  
    64.         init(context, attrs);  
    65.     }  
    66.   
    67.     public RippleView(Context context, AttributeSet attrs, int defStyle) {  
    68.         super(context, attrs, defStyle);  
    69.         init(context, attrs);  
    70.     }  
    71.   
    72.     /**  
    73.      * Method that initializes all fields and sets listeners  
    74.      *  
    75.      * @param context Context used to create this view  
    76.      * @param attrs Attribute used to initialize fields  
    77.      */  
    78.     private void init(final Context context, final AttributeSet attrs) {  
    79.         if (isInEditMode())  
    80.             return;  
    81.   
    82.         final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RippleView);  
    83.         rippleColor = typedArray.getColor(R.styleable.RippleView_rv_color, getResources().getColor(R.color.rippelColor));  
    84.         rippleType = typedArray.getInt(R.styleable.RippleView_rv_type, 0);  
    85.         hasToZoom = typedArray.getBoolean(R.styleable.RippleView_rv_zoom, false);  
    86.         isCentered = typedArray.getBoolean(R.styleable.RippleView_rv_centered, false);  
    87.         rippleDuration = typedArray.getInteger(R.styleable.RippleView_rv_rippleDuration, rippleDuration);  
    88.         frameRate = typedArray.getInteger(R.styleable.RippleView_rv_framerate, frameRate);  
    89.         rippleAlpha = typedArray.getInteger(R.styleable.RippleView_rv_alpha, rippleAlpha);  
    90.         ripplePadding = typedArray.getDimensionPixelSize(R.styleable.RippleView_rv_ripplePadding, 0);  
    91.         canvasHandler = new Handler();  
    92.         zoomScale = typedArray.getFloat(R.styleable.RippleView_rv_zoomScale, 1.03f);  
    93.         zoomDuration = typedArray.getInt(R.styleable.RippleView_rv_zoomDuration, 200);  
    94.         typedArray.recycle();  
    95.         paint = new Paint();  
    96.         paint.setAntiAlias(true);  
    97.         paint.setStyle(Paint.Style.FILL_AND_STROKE);  
    98.         paint.setColor(rippleColor);  
    99.         paint.setAlpha(rippleAlpha);  
    100.         this.setWillNotDraw(false);  
    101.   
    102.         gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {  
    103.             @Override  
    104.             public void onLongPress(MotionEvent event) {  
    105.                 super.onLongPress(event);  
    106.                 animateRipple(event);  
    107.                 sendClickEvent(true);  
    108.             }  
    109.   
    110.             @Override  
    111.             public boolean onSingleTapConfirmed(MotionEvent e) {  
    112.                 return true;  
    113.             }  
    114.   
    115.             @Override  
    116.             public boolean onSingleTapUp(MotionEvent e) {  
    117.                 return true;  
    118.             }  
    119.         });  
    120.   
    121.         this.setDrawingCacheEnabled(true);  
    122.         this.setClickable(true);  
    123.     }  
    124.   
    125.     @Override  
    126.     public void draw(Canvas canvas) {  
    127.         super.draw(canvas);  
    128.         if (animationRunning) {  
    129.             canvas.save();  
    130.             if (rippleDuration <= timer * frameRate) {  
    131.                 animationRunning = false;  
    132.                 timer = 0;  
    133.                 durationEmpty = -1;  
    134.                 timerEmpty = 0;  
    135.                 // There is problem on Android M where canvas.restore() seems to be called automatically  
    136.                 // For now, don't call canvas.restore() manually on Android M (API 23)  
    137.                 if(Build.VERSION.SDK_INT != 23) {  
    138.                     canvas.restore();  
    139.                 }  
    140.                 invalidate();  
    141.                 if (onCompletionListener != null) onCompletionListener.onComplete(this);  
    142.                 return;  
    143.             } else  
    144.                 canvasHandler.postDelayed(runnable, frameRate);  
    145.   
    146.             if (timer == 0)  
    147.                 canvas.save();  
    148.   
    149.   
    150.             canvas.drawCircle(x, y, (radiusMax * (((float) timer * frameRate) / rippleDuration)), paint);  
    151.   
    152.             paint.setColor(Color.parseColor("#ffff4444"));  
    153.   
    154.             if (rippleType == 1 && originBitmap != null && (((float) timer * frameRate) / rippleDuration) > 0.4f) {  
    155.                 if (durationEmpty == -1)  
    156.                     durationEmpty = rippleDuration - timer * frameRate;  
    157.   
    158.                 timerEmpty++;  
    159.                 final Bitmap tmpBitmap = getCircleBitmap((int) ((radiusMax) * (((float) timerEmpty * frameRate) / (durationEmpty))));  
    160.                 canvas.drawBitmap(tmpBitmap, 0, 0, paint);  
    161.                 tmpBitmap.recycle();  
    162.             }  
    163.   
    164.             paint.setColor(rippleColor);  
    165.   
    166.             if (rippleType == 1) {  
    167.                 if ((((float) timer * frameRate) / rippleDuration) > 0.6f)  
    168.                     paint.setAlpha((int) (rippleAlpha - ((rippleAlpha) * (((float) timerEmpty * frameRate) / (durationEmpty)))));  
    169.                 else  
    170.                     paint.setAlpha(rippleAlpha);  
    171.             }  
    172.             else  
    173.                 paint.setAlpha((int) (rippleAlpha - ((rippleAlpha) * (((float) timer * frameRate) / rippleDuration))));  
    174.   
    175.             timer++;  
    176.         }  
    177.     }  
    178.   
    179.     @Override  
    180.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
    181.         super.onSizeChanged(w, h, oldw, oldh);  
    182.         WIDTH = w;  
    183.         HEIGHT = h;  
    184.   
    185.         scaleAnimation = new ScaleAnimation(1.0f, zoomScale, 1.0f, zoomScale, w / 2, h / 2);  
    186.         scaleAnimation.setDuration(zoomDuration);  
    187.         scaleAnimation.setRepeatMode(Animation.REVERSE);  
    188.         scaleAnimation.setRepeatCount(1);  
    189.     }  
    190.   
    191.     /**  
    192.      * Launch Ripple animation for the current view with a MotionEvent  
    193.      *  
    194.      * @param event MotionEvent registered by the Ripple gesture listener  
    195.      */  
    196.     public void animateRipple(MotionEvent event) {  
    197.         createAnimation(event.getX(), event.getY());  
    198.     }  
    199.   
    200.     /**  
    201.      * Launch Ripple animation for the current view centered at x and y position  
    202.      *  
    203.      * @param x Horizontal position of the ripple center  
    204.      * @param y Vertical position of the ripple center  
    205.      */  
    206.     public void animateRipple(final float x, final float y) {  
    207.         createAnimation(x, y);  
    208.     }  
    209.   
    210.     /**  
    211.      * Create Ripple animation centered at x, y  
    212.      *  
    213.      * @param x Horizontal position of the ripple center  
    214.      * @param y Vertical position of the ripple center  
    215.      */  
    216.     private void createAnimation(final float x, final float y) {  
    217.         if (this.isEnabled() && !animationRunning) {  
    218.             if (hasToZoom)  
    219.                 this.startAnimation(scaleAnimation);  
    220.   
    221.             radiusMax = Math.max(WIDTH, HEIGHT);  
    222.   
    223.             if (rippleType != 2)  
    224.                 radiusMax /= 2;  
    225.   
    226.             radiusMax -ripplePadding;  
    227.   
    228.             if (isCentered || rippleType == 1) {  
    229.                 this.x = getMeasuredWidth() / 2;  
    230.                 this.y = getMeasuredHeight() / 2;  
    231.             } else {  
    232.                 this.x = x;  
    233.                 this.y = y;  
    234.             }  
    235.   
    236.             animationRunning = true;  
    237.   
    238.             if (rippleType == 1 && originBitmap == null)  
    239.                 originBitmap = getDrawingCache(true);  
    240.   
    241.             invalidate();  
    242.         }  
    243.     }  
    244.   
    245.     @Override  
    246.     public boolean onTouchEvent(MotionEvent event) {  
    247.         if (gestureDetector.onTouchEvent(event)) {  
    248.             animateRipple(event);  
    249.             sendClickEvent(false);  
    250.         }  
    251.         return super.onTouchEvent(event);  
    252.     }  
    253.   
    254.     @Override  
    255.     public boolean onInterceptTouchEvent(MotionEvent event) {  
    256.         this.onTouchEvent(event);  
    257.         return super.onInterceptTouchEvent(event);  
    258.     }  
    259.   
    260.     /**  
    261.      * Send a click event if parent view is a Listview instance  
    262.      *  
    263.      * @param isLongClick Is the event a long click ?  
    264.      */  
    265.     private void sendClickEvent(final Boolean isLongClick) {  
    266.         if (getParent() instanceof AdapterView) {  
    267.             final AdapterView adapterView = (AdapterView) getParent();  
    268.             final int position = adapterView.getPositionForView(this);  
    269.             final long id = adapterView.getItemIdAtPosition(position);  
    270.             if (isLongClick) {  
    271.                 if (adapterView.getOnItemLongClickListener() != null)  
    272.                     adapterView.getOnItemLongClickListener().onItemLongClick(adapterView, this, position, id);  
    273.             } else {  
    274.                 if (adapterView.getOnItemClickListener() != null)  
    275.                     adapterView.getOnItemClickListener().onItemClick(adapterView, this, position, id);  
    276.             }  
    277.         }  
    278.     }  
    279.   
    280.     private Bitmap getCircleBitmap(final int radius) {  
    281.         final Bitmap output = Bitmap.createBitmap(originBitmap.getWidth(), originBitmap.getHeight(), Bitmap.Config.ARGB_8888);  
    282.         final Canvas canvas = new Canvas(output);  
    283.         final Paint paint = new Paint();  
    284.         final Rect rect = new Rect((int)(x - radius), (int)(y - radius), (int)(x + radius), (int)(y + radius));  
    285.   
    286.         paint.setAntiAlias(true);  
    287.         canvas.drawARGB(0, 0, 0, 0);  
    288.         canvas.drawCircle(x, y, radius, paint);  
    289.   
    290.         paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));  
    291.         canvas.drawBitmap(originBitmap, rect, rect, paint);  
    292.   
    293.         return output;  
    294.     }  
    295.   
    296.     /**  
    297.      * Set Ripple color, default is #FFFFFF  
    298.      *  
    299.      * @param rippleColor New color resource  
    300.      */  
    301.      
    302.     public void setRippleColor(int rippleColor) {  
    303.         this.rippleColor = getResources().getColor(rippleColor);  
    304.     }  
    305.   
    306.     public int getRippleColor() {  
    307.         return rippleColor;  
    308.     }  
    309.   
    310.     public RippleType getRippleType()  
    311.     {  
    312.         return RippleType.values()[rippleType];  
    313.     }  
    314.   
    315.     /**  
    316.      * Set Ripple type, default is RippleType.SIMPLE  
    317.      *  
    318.      * @param rippleType New Ripple type for next animation  
    319.      */  
    320.     public void setRippleType(final RippleType rippleType)  
    321.     {  
    322.         this.rippleType = rippleType.ordinal();  
    323.     }  
    324.   
    325.     public Boolean isCentered()  
    326.     {  
    327.         return isCentered;  
    328.     }  
    329.   
    330.     /**  
    331.      * Set if ripple animation has to be centered in its parent view or not, default is False  
    332.      *  
    333.      * @param isCentered  
    334.      */  
    335.     public void setCentered(final Boolean isCentered)  
    336.     {  
    337.         this.isCentered = isCentered;  
    338.     }  
    339.   
    340.     public int getRipplePadding()  
    341.     {  
    342.         return ripplePadding;  
    343.     }  
    344.   
    345.     /**  
    346.      * Set Ripple padding if you want to avoid some graphic glitch  
    347.      *  
    348.      * @param ripplePadding New Ripple padding in pixel, default is 0px  
    349.      */  
    350.     public void setRipplePadding(int ripplePadding)  
    351.     {  
    352.         this.ripplePadding = ripplePadding;  
    353.     }  
    354.   
    355.     public Boolean isZooming()  
    356.     {  
    357.         return hasToZoom;  
    358.     }  
    359.   
    360.     /**  
    361.      * At the end of Ripple effect, the child views has to zoom  
    362.      *  
    363.      * @param hasToZoom Do the child views have to zoom ? default is False  
    364.      */  
    365.     public void setZooming(Boolean hasToZoom)  
    366.     {  
    367.         this.hasToZoom = hasToZoom;  
    368.     }  
    369.   
    370.     public float getZoomScale()  
    371.     {  
    372.         return zoomScale;  
    373.     }  
    374.   
    375.     /**  
    376.      * Scale of the end animation  
    377.      *  
    378.      * @param zoomScale Value of scale animation, default is 1.03f  
    379.      */  
    380.     public void setZoomScale(float zoomScale)  
    381.     {  
    382.         this.zoomScale = zoomScale;  
    383.     }  
    384.   
    385.     public int getZoomDuration()  
    386.     {  
    387.         return zoomDuration;  
    388.     }  
    389.   
    390.     /**  
    391.      * Duration of the ending animation in ms  
    392.      *  
    393.      * @param zoomDuration Duration, default is 200ms  
    394.      */  
    395.     public void setZoomDuration(int zoomDuration)  
    396.     {  
    397.         this.zoomDuration = zoomDuration;  
    398.     }  
    399.   
    400.     public int getRippleDuration()  
    401.     {  
    402.         return rippleDuration;  
    403.     }  
    404.   
    405.     /**  
    406.      * Duration of the Ripple animation in ms  
    407.      *  
    408.      * @param rippleDuration Duration, default is 400ms  
    409.      */  
    410.     public void setRippleDuration(int rippleDuration)  
    411.     {  
    412.         this.rippleDuration = rippleDuration;  
    413.     }  
    414.   
    415.     public int getFrameRate()  
    416.     {  
    417.         return frameRate;  
    418.     }  
    419.   
    420.     /**  
    421.      * Set framerate for Ripple animation  
    422.      *  
    423.      * @param frameRate New framerate value, default is 10  
    424.      */  
    425.     public void setFrameRate(int frameRate)  
    426.     {  
    427.         this.frameRate = frameRate;  
    428.     }  
    429.   
    430.     public int getRippleAlpha()  
    431.     {  
    432.         return rippleAlpha;  
    433.     }  
    434.   
    435.     /**  
    436.      * Set alpha for ripple effect color  
    437.      *  
    438.      * @param rippleAlpha Alpha value between 0 and 255, default is 90  
    439.      */  
    440.     public void setRippleAlpha(int rippleAlpha)  
    441.     {  
    442.         this.rippleAlpha = rippleAlpha;  
    443.     }  
    444.   
    445.     public void setOnRippleCompleteListener(OnRippleCompleteListener listener) {  
    446.         this.onCompletionListener = listener;  
    447.     }  
    448.   
    449.     /**  
    450.      * Defines a callback called at the end of the Ripple effect  
    451.      */  
    452.     public interface OnRippleCompleteListener {  
    453.         void onComplete(RippleView rippleView);  
    454.     }  
    455.   
    456.     public enum RippleType {  
    457.         SIMPLE(0),  
    458.         DOUBLE(1),  
    459.         RECTANGLE(2);  
    460.   
    461.         int type;  
    462.   
    463.         RippleType(int type)  
    464.         {  
    465.             this.type = type;  
    466.         }  
    467.     }  
    468. }  
  1.  
  2. import android.content.Context;  
  3. import android.content.res.TypedArray;  
  4. import android.graphics.Bitmap;  
  5. import android.graphics.Canvas;  
  6. import android.graphics.Color;  
  7. import android.graphics.Paint;  
  8. import android.graphics.PorterDuff;  
  9. import android.graphics.PorterDuffXfermode;  
  10. import android.graphics.Rect;  
  11. import android.os.Build;  
  12. import android.os.Handler;  
  13. import android.util.AttributeSet;  
  14. import android.view.GestureDetector;  
  15. import android.view.MotionEvent;  
  16. import android.view.animation.Animation;  
  17. import android.view.animation.ScaleAnimation;  
  18. import android.widget.AdapterView;  
  19. import android.widget.RelativeLayout;  
  20.   
  21. /**  
  22.  * Created by Administrator on 2016/5/3.  
  23.  */  
  24. public class RippleView extends RelativeLayout{  
  25.   
  26.     private int WIDTH;  
  27.     private int HEIGHT;  
  28.     private int frameRate = 10;  
  29.     private int rippleDuration = 400;  
  30.     private int rippleAlpha = 90;  
  31.     private Handler canvasHandler;  
  32.     private float radiusMax = 0;  
  33.     private boolean animationRunning = false;  
  34.     private int timer = 0;  
  35.     private int timerEmpty = 0;  
  36.     private int durationEmpty = -1;  
  37.     private float x = -1;  
  38.     private float y = -1;  
  39.     private int zoomDuration;  
  40.     private float zoomScale;  
  41.     private ScaleAnimation scaleAnimation;  
  42.     private Boolean hasToZoom;  
  43.     private Boolean isCentered;  
  44.     private Integer rippleType;  
  45.     private Paint paint;  
  46.     private Bitmap originBitmap;  
  47.     private int rippleColor;  
  48.     private int ripplePadding;  
  49.     private GestureDetector gestureDetector;  
  50.     private final Runnable runnable = new Runnable() {  
  51.         @Override  
  52.         public void run() {  
  53.             invalidate();  
  54.         }  
  55.     };  
  56.   
  57.     private OnRippleCompleteListener onCompletionListener;  
  58.   
  59.     public RippleView(Context context) {  
  60.         super(context);  
  61.     }  
  62.   
  63.     public RippleView(Context context, AttributeSet attrs) {  
  64.         super(context, attrs);  
  65.         init(context, attrs);  
  66.     }  
  67.   
  68.     public RippleView(Context context, AttributeSet attrs, int defStyle) {  
  69.         super(context, attrs, defStyle);  
  70.         init(context, attrs);  
  71.     }  
  72.   
  73.     /**  
  74.      * Method that initializes all fields and sets listeners  
  75.      *  
  76.      * @param context Context used to create this view  
  77.      * @param attrs Attribute used to initialize fields  
  78.      */  
  79.     private void init(final Context context, final AttributeSet attrs) {  
  80.         if (isInEditMode())  
  81.             return;  
  82.   
  83.         final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RippleView);  
  84.         rippleColor = typedArray.getColor(R.styleable.RippleView_rv_color, getResources().getColor(R.color.rippelColor));  
  85.         rippleType = typedArray.getInt(R.styleable.RippleView_rv_type, 0);  
  86.         hasToZoom = typedArray.getBoolean(R.styleable.RippleView_rv_zoom, false);  
  87.         isCentered = typedArray.getBoolean(R.styleable.RippleView_rv_centered, false);  
  88.         rippleDuration = typedArray.getInteger(R.styleable.RippleView_rv_rippleDuration, rippleDuration);  
  89.         frameRate = typedArray.getInteger(R.styleable.RippleView_rv_framerate, frameRate);  
  90.         rippleAlpha = typedArray.getInteger(R.styleable.RippleView_rv_alpha, rippleAlpha);  
  91.         ripplePadding = typedArray.getDimensionPixelSize(R.styleable.RippleView_rv_ripplePadding, 0);  
  92.         canvasHandler = new Handler();  
  93.         zoomScale = typedArray.getFloat(R.styleable.RippleView_rv_zoomScale, 1.03f);  
  94.         zoomDuration = typedArray.getInt(R.styleable.RippleView_rv_zoomDuration, 200);  
  95.         typedArray.recycle();  
  96.         paint = new Paint();  
  97.         paint.setAntiAlias(true);  
  98.         paint.setStyle(Paint.Style.FILL_AND_STROKE);  
  99.         paint.setColor(rippleColor);  
  100.         paint.setAlpha(rippleAlpha);  
  101.         this.setWillNotDraw(false);  
  102.   
  103.         gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {  
  104.             @Override  
  105.             public void onLongPress(MotionEvent event) {  
  106.                 super.onLongPress(event);  
  107.                 animateRipple(event);  
  108.                 sendClickEvent(true);  
  109.             }  
  110.   
  111.             @Override  
  112.             public boolean onSingleTapConfirmed(MotionEvent e) {  
  113.                 return true;  
  114.             }  
  115.   
  116.             @Override  
  117.             public boolean onSingleTapUp(MotionEvent e) {  
  118.                 return true;  
  119.             }  
  120.         });  
  121.   
  122.         this.setDrawingCacheEnabled(true);  
  123.         this.setClickable(true);  
  124.     }  
  125.   
  126.     @Override  
  127.     public void draw(Canvas canvas) {  
  128.         super.draw(canvas);  
  129.         if (animationRunning) {  
  130.             canvas.save();  
  131.             if (rippleDuration <= timer * frameRate) {  
  132.                 animationRunning = false;  
  133.                 timer = 0;  
  134.                 durationEmpty = -1;  
  135.                 timerEmpty = 0;  
  136.                 // There is problem on Android M where canvas.restore() seems to be called automatically  
  137.                 // For now, don't call canvas.restore() manually on Android M (API 23)  
  138.                 if(Build.VERSION.SDK_INT != 23) {  
  139.                     canvas.restore();  
  140.                 }  
  141.                 invalidate();  
  142.                 if (onCompletionListener != null) onCompletionListener.onComplete(this);  
  143.                 return;  
  144.             } else  
  145.                 canvasHandler.postDelayed(runnable, frameRate);  
  146.   
  147.             if (timer == 0)  
  148.                 canvas.save();  
  149.   
  150.   
  151.             canvas.drawCircle(x, y, (radiusMax * (((float) timer * frameRate) / rippleDuration)), paint);  
  152.   
  153.             paint.setColor(Color.parseColor("#ffff4444"));  
  154.   
  155.             if (rippleType == 1 && originBitmap != null && (((float) timer * frameRate) / rippleDuration) > 0.4f) {  
  156.                 if (durationEmpty == -1)  
  157.                     durationEmpty = rippleDuration - timer * frameRate;  
  158.   
  159.                 timerEmpty++;  
  160.                 final Bitmap tmpBitmap = getCircleBitmap((int) ((radiusMax) * (((float) timerEmpty * frameRate) / (durationEmpty))));  
  161.                 canvas.drawBitmap(tmpBitmap, 0, 0, paint);  
  162.                 tmpBitmap.recycle();  
  163.             }  
  164.   
  165.             paint.setColor(rippleColor);  
  166.   
  167.             if (rippleType == 1) {  
  168.                 if ((((float) timer * frameRate) / rippleDuration) > 0.6f)  
  169.                     paint.setAlpha((int) (rippleAlpha - ((rippleAlpha) * (((float) timerEmpty * frameRate) / (durationEmpty)))));  
  170.                 else  
  171.                     paint.setAlpha(rippleAlpha);  
  172.             }  
  173.             else  
  174.                 paint.setAlpha((int) (rippleAlpha - ((rippleAlpha) * (((float) timer * frameRate) / rippleDuration))));  
  175.   
  176.             timer++;  
  177.         }  
  178.     }  
  179.   
  180.     @Override  
  181.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
  182.         super.onSizeChanged(w, h, oldw, oldh);  
  183.         WIDTH = w;  
  184.         HEIGHT = h;  
  185.   
  186.         scaleAnimation = new ScaleAnimation(1.0f, zoomScale, 1.0f, zoomScale, w / 2, h / 2);  
  187.         scaleAnimation.setDuration(zoomDuration);  
  188.         scaleAnimation.setRepeatMode(Animation.REVERSE);  
  189.         scaleAnimation.setRepeatCount(1);  
  190.     }  
  191.   
  192.     /**  
  193.      * Launch Ripple animation for the current view with a MotionEvent  
  194.      *  
  195.      * @param event MotionEvent registered by the Ripple gesture listener  
  196.      */  
  197.     public void animateRipple(MotionEvent event) {  
  198.         createAnimation(event.getX(), event.getY());  
  199.     }  
  200.   
  201.     /**  
  202.      * Launch Ripple animation for the current view centered at x and y position  
  203.      *  
  204.      * @param x Horizontal position of the ripple center  
  205.      * @param y Vertical position of the ripple center  
  206.      */  
  207.     public void animateRipple(final float x, final float y) {  
  208.         createAnimation(x, y);  
  209.     }  
  210.   
  211.     /**  
  212.      * Create Ripple animation centered at x, y  
  213.      *  
  214.      * @param x Horizontal position of the ripple center  
  215.      * @param y Vertical position of the ripple center  
  216.      */  
  217.     private void createAnimation(final float x, final float y) {  
  218.         if (this.isEnabled() && !animationRunning) {  
  219.             if (hasToZoom)  
  220.                 this.startAnimation(scaleAnimation);  
  221.   
  222.             radiusMax = Math.max(WIDTH, HEIGHT);  
  223.   
  224.             if (rippleType != 2)  
  225.                 radiusMax /= 2;  
  226.   
  227.             radiusMax -ripplePadding;  
  228.   
  229.             if (isCentered || rippleType == 1) {  
  230.                 this.x = getMeasuredWidth() / 2;  
  231.                 this.y = getMeasuredHeight() / 2;  
  232.             } else {  
  233.                 this.x = x;  
  234.                 this.y = y;  
  235.             }  
  236.   
  237.             animationRunning = true;  
  238.   
  239.             if (rippleType == 1 && originBitmap == null)  
  240.                 originBitmap = getDrawingCache(true);  
  241.   
  242.             invalidate();  
  243.         }  
  244.     }  
  245.   
  246.     @Override  
  247.     public boolean onTouchEvent(MotionEvent event) {  
  248.         if (gestureDetector.onTouchEvent(event)) {  
  249.             animateRipple(event);  
  250.             sendClickEvent(false);  
  251.         }  
  252.         return super.onTouchEvent(event);  
  253.     }  
  254.   
  255.     @Override  
  256.     public boolean onInterceptTouchEvent(MotionEvent event) {  
  257.         this.onTouchEvent(event);  
  258.         return super.onInterceptTouchEvent(event);  
  259.     }  
  260.   
  261.     /**  
  262.      * Send a click event if parent view is a Listview instance  
  263.      *  
  264.      * @param isLongClick Is the event a long click ?  
  265.      */  
  266.     private void sendClickEvent(final Boolean isLongClick) {  
  267.         if (getParent() instanceof AdapterView) {  
  268.             final AdapterView adapterView = (AdapterView) getParent();  
  269.             final int position = adapterView.getPositionForView(this);  
  270.             final long id = adapterView.getItemIdAtPosition(position);  
  271.             if (isLongClick) {  
  272.                 if (adapterView.getOnItemLongClickListener() != null)  
  273.                     adapterView.getOnItemLongClickListener().onItemLongClick(adapterView, this, position, id);  
  274.             } else {  
  275.                 if (adapterView.getOnItemClickListener() != null)  
  276.                     adapterView.getOnItemClickListener().onItemClick(adapterView, this, position, id);  
  277.             }  
  278.         }  
  279.     }  
  280.   
  281.     private Bitmap getCircleBitmap(final int radius) {  
  282.         final Bitmap output = Bitmap.createBitmap(originBitmap.getWidth(), originBitmap.getHeight(), Bitmap.Config.ARGB_8888);  
  283.         final Canvas canvas = new Canvas(output);  
  284.         final Paint paint = new Paint();  
  285.         final Rect rect = new Rect((int)(x - radius), (int)(y - radius), (int)(x + radius), (int)(y + radius));  
  286.   
  287.         paint.setAntiAlias(true);  
  288.         canvas.drawARGB(0, 0, 0, 0);  
  289.         canvas.drawCircle(x, y, radius, paint);  
  290.   
  291.         paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));  
  292.         canvas.drawBitmap(originBitmap, rect, rect, paint);  
  293.   
  294.         return output;  
  295.     }  
  296.   
  297.     /**  
  298.      * Set Ripple color, default is #FFFFFF  
  299.      *  
  300.      * @param rippleColor New color resource  
  301.      */  
  302.      
  303.     public void setRippleColor(int rippleColor) {  
  304.         this.rippleColor = getResources().getColor(rippleColor);  
  305.     }  
  306.   
  307.     public int getRippleColor() {  
  308.         return rippleColor;  
  309.     }  
  310.   
  311.     public RippleType getRippleType()  
  312.     {  
  313.         return RippleType.values()[rippleType];  
  314.     }  
  315.   
  316.     /**  
  317.      * Set Ripple type, default is RippleType.SIMPLE  
  318.      *  
  319.      * @param rippleType New Ripple type for next animation  
  320.      */  
  321.     public void setRippleType(final RippleType rippleType)  
  322.     {  
  323.         this.rippleType = rippleType.ordinal();  
  324.     }  
  325.   
  326.     public Boolean isCentered()  
  327.     {  
  328.         return isCentered;  
  329.     }  
  330.   
  331.     /**  
  332.      * Set if ripple animation has to be centered in its parent view or not, default is False  
  333.      *  
  334.      * @param isCentered  
  335.      */  
  336.     public void setCentered(final Boolean isCentered)  
  337.     {  
  338.         this.isCentered = isCentered;  
  339.     }  
  340.   
  341.     public int getRipplePadding()  
  342.     {  
  343.         return ripplePadding;  
  344.     }  
  345.   
  346.     /**  
  347.      * Set Ripple padding if you want to avoid some graphic glitch  
  348.      *  
  349.      * @param ripplePadding New Ripple padding in pixel, default is 0px  
  350.      */  
  351.     public void setRipplePadding(int ripplePadding)  
  352.     {  
  353.         this.ripplePadding = ripplePadding;  
  354.     }  
  355.   
  356.     public Boolean isZooming()  
  357.     {  
  358.         return hasToZoom;  
  359.     }  
  360.   
  361.     /**  
  362.      * At the end of Ripple effect, the child views has to zoom  
  363.      *  
  364.      * @param hasToZoom Do the child views have to zoom ? default is False  
  365.      */  
  366.     public void setZooming(Boolean hasToZoom)  
  367.     {  
  368.         this.hasToZoom = hasToZoom;  
  369.     }  
  370.   
  371.     public float getZoomScale()  
  372.     {  
  373.         return zoomScale;  
  374.     }  
  375.   
  376.     /**  
  377.      * Scale of the end animation  
  378.      *  
  379.      * @param zoomScale Value of scale animation, default is 1.03f  
  380.      */  
  381.     public void setZoomScale(float zoomScale)  
  382.     {  
  383.         this.zoomScale = zoomScale;  
  384.     }  
  385.   
  386.     public int getZoomDuration()  
  387.     {  
  388.         return zoomDuration;  
  389.     }  
  390.   
  391.     /**  
  392.      * Duration of the ending animation in ms  
  393.      *  
  394.      * @param zoomDuration Duration, default is 200ms  
  395.      */  
  396.     public void setZoomDuration(int zoomDuration)  
  397.     {  
  398.         this.zoomDuration = zoomDuration;  
  399.     }  
  400.   
  401.     public int getRippleDuration()  
  402.     {  
  403.         return rippleDuration;  
  404.     }  
  405.   
  406.     /**  
  407.      * Duration of the Ripple animation in ms  
  408.      *  
  409.      * @param rippleDuration Duration, default is 400ms  
  410.      */  
  411.     public void setRippleDuration(int rippleDuration)  
  412.     {  
  413.         this.rippleDuration = rippleDuration;  
  414.     }  
  415.   
  416.     public int getFrameRate()  
  417.     {  
  418.         return frameRate;  
  419.     }  
  420.   
  421.     /**  
  422.      * Set framerate for Ripple animation  
  423.      *  
  424.      * @param frameRate New framerate value, default is 10  
  425.      */  
  426.     public void setFrameRate(int frameRate)  
  427.     {  
  428.         this.frameRate = frameRate;  
  429.     }  
  430.   
  431.     public int getRippleAlpha()  
  432.     {  
  433.         return rippleAlpha;  
  434.     }  
  435.   
  436.     /**  
  437.      * Set alpha for ripple effect color  
  438.      *  
  439.      * @param rippleAlpha Alpha value between 0 and 255, default is 90  
  440.      */  
  441.     public void setRippleAlpha(int rippleAlpha)  
  442.     {  
  443.         this.rippleAlpha = rippleAlpha;  
  444.     }  
  445.   
  446.     public void setOnRippleCompleteListener(OnRippleCompleteListener listener) {  
  447.         this.onCompletionListener = listener;  
  448.     }  
  449.   
  450.     /**  
  451.      * Defines a callback called at the end of the Ripple effect  
  452.      */  
  453.     public interface OnRippleCompleteListener {  
  454.         void onComplete(RippleView rippleView);  
  455.     }  
  456.   
  457.     public enum RippleType {  
  458.         SIMPLE(0),  
  459.         DOUBLE(1),  
  460.         RECTANGLE(2);  
  461.   
  462.         int type;  
  463.   
  464.         RippleType(int type)  
  465.         {  
  466.             this.type = type;  
  467.         }  
  468.     }  
  469. }  
posted @ 2018-03-10 13:47  Faxcom  阅读(547)  评论(0编辑  收藏  举报