自从看大神的"风扇吹树叶"的自定义控件,是根据Gif图来作的。 我看到有意思的Gif图,也会想着该如何实现。
so,从一个简单的开始。
canvas
没什么复杂的东西,圆心是个圆形。一共有三条波纹,从圆形中发出。效果图以下。bash
public class RippleView extends BaseView {
// 绘图
private int mRadius = DisplayUtils.dp2px(this.getContext(), 30);
private int mFirstRip = DisplayUtils.dp2px(this.getContext(), 20);
private int mSecondRip = DisplayUtils.dp2px(this.getContext(), 40);
private int mThirdRip = DisplayUtils.dp2px(this.getContext(), 60);
// 动画
private ValueAnimator mValueAnimator;
private float mAnimationValue;
public RippleView(Context context) {
this(context, null);
}
public RippleView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public RippleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint.setStrokeWidth(DisplayUtils.dp2px(this.getContext(), 1));
mValueAnimator = ValueAnimator.ofFloat(0, 1).setDuration(1000);
mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mAnimationValue = (float) animation.getAnimatedValue();
postInvalidate();
}
});
mValueAnimator.setRepeatCount(-1);
mValueAnimator.start();
}
复制代码
也比较容易理解,View的大小 = 心里圆直径 + 最大波浪的最终位置 + margin + paddingide
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int measuredWidth = width, measuredHeight = height;
if (widthMode != MeasureSpec.EXACTLY) {
measuredWidth = mRadius * 2 + mThirdRip * 2 + mMargin * 2 + mPadding * 2;
}
if (heightMode != MeasureSpec.EXACTLY) {
measuredHeight = mRadius * 2 + mThirdRip * 2 + mMargin * 2 + mPadding * 2;
}
setMeasuredDimension(measuredWidth, measuredHeight);
}
复制代码
绘制心里圆,三个波浪。波浪根据mAnimationValue进行取值。post
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(mViewWidth / 2, mViewHeight / 2);
canvas.save();
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawCircle(0, 0, mRadius, mPaint);
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(0, 0, mRadius + mFirstRip * mAnimationValue, mPaint);
canvas.drawCircle(0, 0, mRadius + mSecondRip * mAnimationValue, mPaint);
canvas.drawCircle(0, 0, mRadius + mThirdRip * mAnimationValue, mPaint);
postInvalidate();
canvas.restore();
}
复制代码
速度最小值为1,最大值为100. 由SeekBar控制。到此结束。动画
public void setSpeed(int speed) {
long l = (long) (1000 * (1-(speed * 1.0f) / 100f));
mValueAnimator.setDuration(l);
}
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
mRippleView.setSpeed(progress);
}
复制代码