package com.bootstrap.widget; import android.animation.Animator; import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.os.Build; import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.view.View; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import com.bootstrap.R; import com.bootstrap.animation.AbstractAnimatorListener; public class RippleView extends View { private float centerX, centerY, maxRadius, radius; private float animValue; private long startDelay, duration; private int repeatCount, repeatMode; private int currentLayerType; private boolean withAlpha, reverseAlpha; private boolean revert; private Paint paint; private Rect boundsRect; private Rect rippleRect; private Runnable endAction; public RippleView(final Context context) { super(context); init(null); } public RippleView(final Context context, final AttributeSet attrs) { super(context, attrs); init(attrs); } public RippleView(final Context context, final AttributeSet attrs, final int defStyleAttr) { super(context, attrs, defStyleAttr); init(attrs); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public RippleView(final Context context, final AttributeSet attrs, final int defStyleAttr, final int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(attrs); } private void init(final AttributeSet attrs) { int color = Color.WHITE; if (attrs != null) { final TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.RippleView); color = array.getColor(R.styleable.RippleView_bgColor, color); array.recycle(); } paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(color); boundsRect = new Rect(); rippleRect = new Rect(); } public RippleView withColor(final int color) { paint.setColor(color); return this; } public RippleView hotSpot(final float centerX, final float centerY, final float maxRadius) { this.centerX = centerX; this.centerY = centerY; this.maxRadius = maxRadius; this.radius = 0.0f; return this; } public RippleView withAlpha() { withAlpha = true; return this; } public RippleView withReverseAlpha() { withAlpha = true; reverseAlpha = true; return this; } public RippleView startDelay(final long delay) { startDelay = delay; return this; } public RippleView duration(final long duration) { this.duration = duration; return this; } public RippleView endAction(final Runnable runnable) { this.endAction = runnable; return this; } public RippleView revert() { this.revert = true; return this; } public RippleView repeat(final int repeatMode, final int repeatCount) { this.repeatMode = repeatMode; this.repeatCount = repeatCount; return this; } public ValueAnimator ripple() { final ValueAnimator animator = ValueAnimator.ofFloat(revert ? 1.0f : 0.0f, revert ? 0.0f : 1.0f); animator.setDuration(duration); animator.setStartDelay(startDelay); animator.setInterpolator(revert ? new AccelerateInterpolator() : new DecelerateInterpolator()); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(final ValueAnimator animation) { animValue = (float) animation.getAnimatedValue(); radius = maxRadius * animValue; if (withAlpha) { paint.setAlpha((int) (255 * (reverseAlpha ? 1.0f - animValue : animValue))); } rippleRect.set((int) (centerX - radius), (int) (centerY - radius), (int) (centerX + radius), (int) (centerY + radius)); if (rippleRect.left < boundsRect.left) { rippleRect.left = boundsRect.left; } if (rippleRect.top < boundsRect.top) { rippleRect.top = boundsRect.top; } if (rippleRect.right > boundsRect.right) { rippleRect.right = boundsRect.right; } if (rippleRect.bottom > boundsRect.bottom) { rippleRect.bottom = boundsRect.bottom; } ViewCompat.postInvalidateOnAnimation(RippleView.this); } }); animator.addListener(new AbstractAnimatorListener() { @Override public void onAnimationStart(final Animator animation) { currentLayerType = getLayerType(); setLayerType(LAYER_TYPE_HARDWARE, null); } @Override public void onAnimationEnd(final Animator animation) { setLayerType(currentLayerType, null); if (endAction != null) { endAction.run(); endAction = null; } revert = false; startDelay = 0l; } }); animator.setRepeatMode(repeatMode); animator.setRepeatCount(repeatCount); return animator; } @Override protected void onDraw(final Canvas canvas) { if (radius > 0.0f) { canvas.clipRect(rippleRect); canvas.drawCircle(centerX, centerY, radius, paint); } } @Override protected void onSizeChanged(final int w, final int h, final int oldw, final int oldh) { super.onSizeChanged(w, h, oldw, oldh); boundsRect.set(0, 0, w, h); } }