自從看大神的"風扇吹樹葉"的自定義控件,是根據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);
}
複製代碼