思路:
重写Button在onTouchEvent中监听action_down事件,然后播放一个属性动画,动态的改变圆圈的半径,这样就产生了波纹效果
1.首先是RippleButton直接继承自Button
2.在attrs.xml中自定义两个属性一个是波纹的颜色rb_rippleColor,一个是波纹的透明度rb_alphaFactor;
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RippleView">
<attr name="rb_rippleColor" format="color" />
<attr name="rb_alphaFactor" format="float" />
</declare-styleable>
</resources>
3.在构造函数中初始化这两个属性
public RippleButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setAlpha(100);
mPath = new Path();
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RippleView);
mRippleColor = a.getColor(R.styleable.RippleView_rb_rippleColor, mRippleColor);
mAlphaFactor = a.getFloat(R.styleable.RippleView_rb_alphaFactor, mAlphaFactor);
if (mAlphaFactor > 0 && mAlphaFactor <= 1) {
mPaint.setAlpha((int) Math.floor(255 * mAlphaFactor));
}
a.recycle();
}
4.重写onTouchEvent
public boolean onTouchEvent(MotionEvent event) {
boolean superResult = super.onTouchEvent(event);
mDownX = event.getX();
mDownY = event.getY();
if (isEnabled() && !isAnimatorPlaying && event.getActionMasked() == MotionEvent.ACTION_DOWN) {
//calculate ripple's radius
float offsetX = Math.max(mDownX - getLeft(), getRight() - mDownX);
float offsetY = Math.max(mDownY - getTop(), getBottom() - mDownY);
mMaxRadius = (float) Math.sqrt(Math.pow(offsetX, 2) + Math.pow(offsetY, 2));
ObjectAnimator mScaleAnimator = ObjectAnimator.ofFloat(this, "radius", 0, mMaxRadius);
mScaleAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
isAnimatorPlaying = true;
}
@Override
public void onAnimationEnd(Animator animation) {
setRadius(0);
isAnimatorPlaying = false;
if (onRippleFinished != null) {
onRippleFinished.onRippleFinished(RippleButton.this);
}
}
@Override
public void onAnimationCancel(Animator animation) {
isAnimatorPlaying = false;
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
mScaleAnimator.setInterpolator(new AccelerateInterpolator());
mScaleAnimator.setDuration(500);
mScaleAnimator.start();
}
return superResult;
}
这里边主要是对手指按下这个事件作出了相应,给一个属性动画,也就是对半径作出改变,下面一个重要的方法就是setRadius(float radius)
5.实现setRadius方法
protected void setRadius(float radius) {
mMaxRadius = radius;
if (radius > 0) {
RadialGradient mRadialGradient = new RadialGradient(mDownX, mDownY, radius, adjustAlpha(mRippleColor, mAlphaFactor), mRippleColor, Shader.TileMode.MIRROR);
mPaint.setShader(mRadialGradient);
}
invalidate();
}
这里面是根据属性动画里不断传递过来的半径,对RadialGradient不断的改变其大小,然后重新绘制真个view
6.更新view
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (isInEditMode()) {
return;
}
canvas.save(Canvas.CLIP_SAVE_FLAG);
mPath.reset();
mPath.addCircle(mDownX, mDownY, mMaxRadius, Path.Direction.CW);
canvas.clipPath(mPath);
canvas.restore();
canvas.drawCircle(mDownX, mDownY, mMaxRadius, mPaint);
}
这里面主要就是根据panint画出一个圆,这样就实现了ripple效果啦
7.最后是给动画结束添加一个回调函数
public interface OnRippleFinished {
void onRippleFinished(View rippleButton);
}
public void setOnRippleFinished(OnRippleFinished onRippleFinished) {
this.onRippleFinished = onRippleFinished;
}
源码地址:https://github.com/zlidentify/RippleButton
参考实现:https://github.com/siriscac/RippleView