贝塞尔曲线
贝塞尔是数学中的一个定义轨迹的东西,由起点、终点、控制点组成。其中控制点可以是一个或者多个,贝塞尔原理在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;
}
}