adnroid贝塞尔实现自定义水波View

贝塞尔曲线

贝塞尔是数学中的一个定义轨迹的东西,由起点、终点、控制点组成。其中控制点可以是一个或者多个,贝塞尔原理在android中可以实现很多有趣的动画效果。下面我就自定义用贝塞尔实现一个水波纹效果的自定义View。

原理图

最终效果图

直接贴代码


public class WaveView extends View {

    /*****画水波纹画笔*****/
    private Paint mPaint;
    /******水波路径*******/
    private Path mPath;
    /******水波个数 注意水波个数至少两个********/
    private int mWaveCount=3;
    /*****view宽********/
    private float mWidth;
    /*****view高******/
    private float mHeight;
    /*****一个水波周期的宽度*********/
    private float mWaveWidth;
    /******贝塞尔控制点 Y轴偏移量 浪高*******/
    private float mOffsetY=50;
    /******水波X轴平移量*******/
    private float mOffsetX;
    /****水波水平线Y轴值*****/
    private float mCenterY;
    /*****一个水波周期的4分之一宽度********/
    private float mSkip;
    /*****水波平移动画*********/
    private ValueAnimator mValueAnimator;
    /*****水波颜色*********/
    private int mWaveColor=Color.BLUE;


    public WaveView(Context context) {
        super(context);
        init();
    }

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

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


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth=w;
        mHeight=h;
        mCenterY=mHeight / 2f;
        mWaveWidth=mWidth / (mWaveCount-1);//因为在屏幕左边屏幕外有一个周期的水波,所以该处应该减1
        mSkip=mWaveWidth / 4;
        startAnimator();
    }

    private void init(){
        mPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mPaint.setColor(mWaveColor);
        mPaint.setStrokeWidth(5f);
        mPath=new Path();
    }


    private void startAnimator(){
        mValueAnimator=ValueAnimator.ofFloat(0,mWaveWidth);
        mValueAnimator.setDuration(1*1000);
        mValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
        mValueAnimator.setInterpolator(new LinearInterpolator());
        mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                mOffsetX=(float)valueAnimator.getAnimatedValue();
                postInvalidate();
            }
        });
        mValueAnimator.start();
    }


    @Override
    protected void onDraw(Canvas canvas) {
       // super.onDraw(canvas);
        canvas.drawColor(Color.GRAY);
        mPath.reset();
        mPath.moveTo(-mWaveWidth,mCenterY);
        for(int i=0;i<mWaveCount;i++){
            mPath.quadTo(-mSkip*3+(mWaveWidth*i)+mOffsetX , mCenterY+mOffsetY , -mSkip*2+(mWaveWidth*i)+mOffsetX , mCenterY);
            mPath.quadTo(-mSkip+(mWaveWidth*i)+mOffsetX , mCenterY-mOffsetY , +(mWaveWidth*i)+mOffsetX , mCenterY);
        }

        mPath.lineTo(mWidth,mHeight);
        mPath.lineTo(0,mHeight);
        mPath.close();
        canvas.drawPath(mPath,mPaint);
    }

}
在上面代码的基础上还可以实现圆形的进度效果

代码如下

public class WaveView extends View {

    /*****画水波纹画笔*****/
    private Paint mPaint;
    /******水波路径*******/
    private Path mPath;
    /******水波个数 注意水波个数至少两个********/
    private int mWaveCount=3;
    /*****view宽********/
    private float mWidth;
    /*****view高******/
    private float mHeight;
    /*****一个水波周期的宽度*********/
    private float mWaveWidth;
    /******贝塞尔控制点 Y轴偏移量 浪高*******/
    private float mOffsetY=50;
    /******水波X轴平移量*******/
    private float mOffsetX;
    /****水波水平线Y轴值*****/
    private float mCenterY;
    /*****一个水波周期的4分之一宽度********/
    private float mSkip;
    /*****水波平移动画*********/
    private ValueAnimator mValueAnimatorX;
    /*****水波颜色*********/
    private int mWaveColor=Color.BLUE;

    /*****底部水波纹bitmap*****/
    private Bitmap mBitmap;
    /******XFerMode画笔*******/
    private Paint  mXferModePaint;
    /*****画组合Bitmap的画布*******/
    private Canvas mCanvas;
    /*****前景过滤bitmap********/
    private Bitmap mForegroundBitmap;
    /******前景bitmap的颜色*****/
    private int mForegroundColor=Color.WHITE;

    /*****水波纹Y轴动画******/
    private ValueAnimator mValueAnimatorY;
    /*****水平移动周期时间******/
    private int mTimeX=1000;
    /*****Y轴移动周期时间********/
    private int mTimeY=15*1000;

    public WaveView(Context context) {
        super(context);
        init();
    }

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

    public WaveView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
   private void init(){
        mPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mPaint.setColor(mWaveColor);
        mPaint.setStrokeWidth(5f);

        mXferModePaint=new Paint(Paint.ANTI_ALIAS_FLAG);
        mXferModePaint.setAntiAlias(true);
        mXferModePaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mXferModePaint.setStrokeWidth(5f);
        mXferModePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));

        mPath=new Path();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth=w;
        mHeight=h;
        mCenterY=mHeight / 1f;
        mWaveWidth=mWidth / (mWaveCount-1);//因为在屏幕左边屏幕外有一个周期的水波,所以该处应该减1
        mSkip=mWaveWidth / 4;
        mBitmap=Bitmap.createBitmap((int)mWidth,(int)mHeight, Bitmap.Config.ARGB_8888);
        mCanvas=new Canvas(mBitmap);
        mForegroundBitmap=getForegroundBitmap((int)mWidth,(int)mHeight);
        startAnimator();
    }

 


    private void startAnimator(){
        mValueAnimatorX=ValueAnimator.ofFloat(0,mWaveWidth);
        mValueAnimatorX.setDuration(mTimeX);
        mValueAnimatorX.setRepeatCount(ValueAnimator.INFINITE);
        mValueAnimatorX.setInterpolator(new LinearInterpolator());
        mValueAnimatorX.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                mOffsetX=(float)valueAnimator.getAnimatedValue();
                postInvalidate();
            }
        });
        mValueAnimatorX.start();


        mValueAnimatorY=ValueAnimator.ofFloat(mHeight,0);
        mValueAnimatorY.setDuration(mTimeY);
        mValueAnimatorY.setRepeatCount(ValueAnimator.INFINITE);
        mValueAnimatorY.setInterpolator(new LinearInterpolator());
        mValueAnimatorY.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                mCenterY=(float)valueAnimator.getAnimatedValue();
            }
        });
        mValueAnimatorY.start();
    }


    @Override
    protected void onDraw(Canvas canvas) {
       // super.onDraw(canvas);
        mPaint.setColor(mWaveColor);
        canvas.drawColor(Color.GRAY);
        mPath.reset();
        mPath.moveTo(-mWaveWidth,mCenterY);
        for(int i=0;i<mWaveCount;i++){
            mPath.quadTo(-mSkip*3+(mWaveWidth*i)+mOffsetX , mCenterY+mOffsetY , -mSkip*2+(mWaveWidth*i)+mOffsetX , mCenterY);
            mPath.quadTo(-mSkip+(mWaveWidth*i)+mOffsetX , mCenterY-mOffsetY , +(mWaveWidth*i)+mOffsetX , mCenterY);
        }
        mPath.lineTo(mWidth,mHeight);
        mPath.lineTo(0,mHeight);
        mPath.close();
        //擦除底部水波纹画布之前画的bitmap
        mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        //画水波纹bitmap
        mCanvas.drawPath(mPath,mPaint);
        //画前景bitmap
        mCanvas.drawBitmap(mForegroundBitmap,0,0,mXferModePaint);
        canvas.drawBitmap(mBitmap,0,0,mPaint);
    }


    /**
     * 获取前景bitmap
     * @param width
     * @param height
     * @return
     */
    private Bitmap getForegroundBitmap(int width,int height){
        mPaint.setColor(mForegroundColor);
        Bitmap mBitmap=  Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
        Canvas canvas=new Canvas(mBitmap);
        int radius=Math.min(width,height)/2;
        canvas.drawCircle(width/2,height/2,radius,mPaint);
        return mBitmap;
    }


}



最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 173,302评论 25 708
  • 每次听到某大牛谈论自定义View,顿时敬佩之心,如滔滔江水连绵不绝,心想我什么时候能有如此境界,好了,心动不如行动...
    Code4Android阅读 2,182评论 6 25
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,241评论 4 61
  • 哈哈
    4bad29680856阅读 148评论 0 0
  • DDL CREATE TABLE [IF NOT EXISTS] 表名称( 字段名1 列类型 [属性]...
    zz张哲阅读 455评论 0 0