一个水波纹动画的实现

效果图

按照往常惯例先看下效果图
图片展示

设计原理

要实现图中的水波纹效果其实也很简单,首先想到的使用ValueAnimator动画来实现。计算最里面的水波纹和最外面的水波纹的距离,然后通过onAnimationUpdate回调获取当前的值画圆,这里我们需要用一个List来保存从动画开始到动画结束所以的值。用来在onDraw()来计算画多少个水波纹。注意这里还有颜色的透明度的变化还有每个水波纹之间的距离也是要考虑的。透明度可以通过距离差值动态的从FF~00设置,每个水波纹之间的距离这就要考虑用何种Interpolator还有就是onAnimationUpdate每回调几次才进行绘制一次来计算了。总体逻辑就是这样。接下来上源代码了

源码

java代码

public class RippleView extends View implements ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener {

    //水波纹开始的半径
    private int mStartRadius;
    //控件的半径
    private int mRadius;

    //水波纹颜色
    private int mColor;
    //线的宽度
    private int mStrokeWidth;

    //一次水波纹的动画时间
    private int mDuration;

   


    //画笔
    private Paint mPaint;
    private static final int DEFAULT_START_RADIUS = 30;
    private static final int DEFAULT_COLOR = 0xFF5BE5E5;
    private static final int DEFAULT_STROKE = 3;
    private static final int DEFAULT_DURAION = 1000;//1秒

    //没有透明度的颜色值
    private int mNoAlphaColor;

    //默认动画差值
    private TimeInterpolator mInterpolator = new AccelerateInterpolator();
    private ValueAnimator mValueAnimator;

    private ArrayList<Integer> mRadiusList = new ArrayList<Integer>(50);

    //统计动画回调的次数
    private int mCount=0;
    //取模次数才进行绘制

    private int mMode;
    private static final int DEFAULT_MODE=8;


    public RippleView(Context context) {
        super(context);
        init(context, null);
    }

    public RippleView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public RippleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attr) {
        TypedArray typedArray = context.obtainStyledAttributes(attr, R.styleable.RippleView, 0, 0);
        mStartRadius = typedArray.getDimensionPixelSize(R.styleable.RippleView_ripple_start_radius, DEFAULT_START_RADIUS);
        mColor = typedArray.getColor(R.styleable.RippleView_ripple_color, DEFAULT_COLOR);
        mStrokeWidth = typedArray.getDimensionPixelSize(R.styleable.RippleView_ripple_stroke, DEFAULT_STROKE);
        mDuration = typedArray.getInteger(R.styleable.RippleView_ripple_duration, DEFAULT_DURAION);
        mMode=typedArray.getInteger(R.styleable.RippleView_ripple_mode,DEFAULT_MODE);
        typedArray.recycle();
        //如果最高位有值得话就说明设置了透明度
        //无符号右移 高位补0
        if ((mColor>>>24) > 0) {
            mNoAlphaColor = mColor & 0x00FFFFFF;
        } else {
            mNoAlphaColor = mColor;
        }
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setColor(mColor);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(mStrokeWidth);

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = measureHanlder(widthMeasureSpec);
        int height = measureHanlder(heightMeasureSpec);
        mRadius = ((width < height) ? width : height) / 2;
        setMeasuredDimension(width, height);
    }

    private int measureHanlder(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else if (specMode == MeasureSpec.AT_MOST) {
            result = Math.max(result, specSize);
        }
        return result;
    }

    /**
     * 开始动画
     */
    public void startAnimation() {
        if (mValueAnimator == null) {
            mValueAnimator = new ValueAnimator();
            mValueAnimator.setInterpolator(mInterpolator);
            mValueAnimator.setIntValues(mStartRadius,
                    mRadius);
            mValueAnimator.setDuration(mDuration);
            mValueAnimator.setRepeatCount(-1);
            mValueAnimator.addUpdateListener(this);
            mValueAnimator.addListener(this);
            mValueAnimator.start();
        } else {
            if (!mValueAnimator.isRunning()) {
                mValueAnimator.start();
            }
        }

    }

    /**
     * 获取透明度
     *
     * @param mChangeRadius
     * @return
     */
    private int getColor(int mChangeRadius) {
        int currentRT = (mRadius - mChangeRadius);
        int sumR = mRadius - mStartRadius;

        int alpha = 255 * currentRT / sumR;

        //把透明度左移24位移动到最高位再进行与运算就得到正在的值啦
        return (alpha << 24) | mNoAlphaColor;

    }

    @Override
    protected void onDetachedFromWindow() {
        stopAnimation();
        super.onDetachedFromWindow();
    }

    /**
     * 结束动画
     */
    public void stopAnimation() {
        if (mValueAnimator != null && mValueAnimator.isRunning()) {
            mValueAnimator.cancel();
        }
    }


    @Override
    protected void onDraw(Canvas canvas) {


        for (int i = 0; i < mRadiusList.size(); i++) {
            int changeRadius = mRadiusList.get(i);
            mPaint.setColor(getColor(changeRadius));
            canvas.drawCircle(mRadius, mRadius, changeRadius, mPaint);

        }
    }

    @Override
    public void onAnimationStart(Animator animation) {
        mRadiusList.clear();

    }

    @Override
    public void onAnimationEnd(Animator animation) {


    }

    @Override
    public void onAnimationCancel(Animator animation) {

        mCount=0;
        mRadiusList.clear();
        invalidate();

    }

    @Override
    public void onAnimationRepeat(Animator animation) {
        mCount=0;
        mRadiusList.clear();
    }

    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        mCount++;
        if(mCount%mMode==0){
            int mRadius = (int) animation.getAnimatedValue();
            Log.e("lx","now Radius=>"+mRadius+"startRadius=>"+mStartRadius+"mRadius=>"+this.mRadius);
            mRadiusList.add(mRadius);
            invalidate();
        }

    }
}

xml中的属性

   <declare-styleable name="RippleView">
        
        <!--水波纹从什么时候开始,开始的半径 -->
        <attr name="ripple_start_radius" format="dimension|reference"/>
        <!--水波纹颜色 -->
        <attr name="ripple_color" format="color|reference"/>
        <!--水波纹粗度 -->
        <attr name="ripple_stroke" format="dimension|reference"/>
        <!--动画的时间 建议1000 毫秒单位 -->
        <attr name="ripple_duration" format="integer"/>

        <!--在动画更新值方法中多少次才取一次数据刷新绘制 建议值8 -->
        <attr name="ripple_mode" format="integer"/>


    </declare-styleable>

总结

代码比较简单,就这样吧

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值