学习过了Android中贝塞尔曲线的画法贝塞尔曲线,我们就利用学到的东西自定义一下view,水波纹进度球也有叫能量球,进度球等。
废话不多说,先上图:

/**
*
* 1.利用path 画圆
* 2.利用path 画波浪,添加动画
* 3.利用path的op()方法,取两个path的交集
* 4.定义动画器,动态更新波浪的起点
*/
public class BezierView3 extends View {
private float cx,cy;//圆心
private Paint paint;//画圆的笔
private float radius;//半径
private Paint mPaint;//画贝塞尔曲线的笔
private Path path;
private ValueAnimator animator;
private int offsetX;
public BezierView3(Context context) {
super(context);
}
public BezierView3(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
paint.setColor(Color.RED);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.BLUE);
path = new Path();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
//屏幕中心为圆心
cx = getWidth()/2f;
cy = getHeight()/2f;
radius = (getWidth()/3f)/2f;
animator = ValueAnimator.ofInt(0, (int) radius*2);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
offsetX=(int)animation.getAnimatedValue();
invalidate();
}
});
animator.setInterpolator(new LinearInterpolator());
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.setDuration(1000);
animator.start();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
path.reset();
Path cPath = new Path();
cPath.addCircle(cx,cy,radius,Path.Direction.CW);
//用切割也可以,不过就是锯齿很明显,所以还是用path.op()比较好
// canvas.clipPath(cPath);
path.moveTo((int)(cx-3*radius)+offsetX,(int) cy);
for (int i = 0; i < 2*radius; i+=radius) {
path.rQuadTo(radius/2, -((float)Math.sqrt(Math.pow(radius,2)-Math.pow(radius/2,2))),radius,0);
path.rQuadTo(radius/2, ((float)Math.sqrt(Math.pow(radius,2)-Math.pow(radius/2,2))),radius,0);
}
path.lineTo(getWidth() ,getHeight());//右下角
path.lineTo(0,getHeight());//左下角
path.close();
mPaint.setStyle(Paint.Style.FILL);
//取两个path路径的交互区域 这种方式比canvas剪切方法要好,这样没有锯齿 ,剪接clip方法有锯齿严重
path.op(cPath, Path.Op.INTERSECT);
canvas.drawCircle(cx,cy,radius,paint);
canvas.drawPath(path,mPaint);
}
}
能够根据上面的代码画出,效果后,我们进一步理解了贝赛尔曲线。接下来我们画一个由进度效果的能量球。
先看效果:

下面代码,注释已经很清楚了
package com.wgp.customview;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.LinearInterpolator;
import androidx.annotation.Nullable;
/**
* 利用Path的rQuadTo()、addCircle()画出进度球
*
* 1.先画出圆形利用 Path的addCircle()方法
* 2.再用path的画贝赛尔曲线的方法把波浪画出来,并添加动画
* 3.两个path路径进行取交集显示,path.op(cpath, Path.Op.INTERSECT);
* 4.动态设置波浪起点的y坐标,跟随下载进度增长
* 5.圆球中间显示文本,先定义一个矩形把圆球框起来,然后计算出文本的基线以及放入中心显示x坐标
*/
public class BezierProgressView extends View {
//圆心坐标
private float cx;
private float cy;
//圆半径
private float raduis=200;
//贝塞尔曲线的paint
private Paint paint;
//画圆的pain
private Paint cpaint;
//绘制贝塞尔曲线的path
private Path path;
//绘制圆的path
private Path cpath;
private ValueAnimator animator;
//x轴方向波浪动画的位移
private int offsetX;
//下载的百分比 加载的百分比
private int percent;
//文本
private Paint txtPaint;
private Rect rect;
private Paint rectPaint;
private int baseLineY;
public BezierProgressView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(5);
paint.setAntiAlias(true);
paint.setColor(Color.BLUE);
path = new Path();
cpath = new Path();
cpaint = new Paint();
cpaint.setStyle(Paint.Style.FILL);
cpaint.setStrokeWidth(5);
cpaint.setAntiAlias(true);
cpaint.setColor(Color.CYAN);
txtPaint = new Paint();
txtPaint.setColor(Color.RED);
txtPaint.setAntiAlias(true);
txtPaint.setStrokeWidth(5);
txtPaint.setStyle(Paint.Style.STROKE);
txtPaint.setTextSize(50);
//该方法即为设置基线上那个点究竟是left,center,还是right 这里我设置为center
txtPaint.setTextAlign(Paint.Align.CENTER);
rectPaint = new Paint();
rectPaint.setColor(Color.TRANSPARENT);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
cx = getWidth()/2f;
cy = getHeight()/2f;
cpath.addCircle(cx,cy,raduis, Path.Direction.CW);
animator = ValueAnimator.ofInt(0, (int) raduis);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.setInterpolator(new LinearInterpolator());
animator.setDuration(500);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//每改变一次数值,我们就刷新一下画面
offsetX = (int) animation.getAnimatedValue();
invalidate();
}
});
animator.start();
//根据画出的矩形 计算 canvas.drawText()方法中需要的x,y坐标,
rect = new Rect((int)(cx-raduis),(int)(cy-raduis),(int)(cx+raduis),(int)(cy+raduis));
Paint.FontMetrics fontMetrics = txtPaint.getFontMetrics();
//为基线到字体上边框的距离
float fontTop = fontMetrics.top;
//为基线到字体下边框的距离
float fontBottom = fontMetrics.bottom;
//基线中间点的y轴计算公式
baseLineY = (int) (rect.centerY() - fontTop/2 - fontBottom/2);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//清空一下每一次画的线条,如果不清空,那么每画一次path,下次还会显示上一次的path画的路径
path.reset();
//模拟下载。真实的下载,这个是百分比数值,百分比数值乘以圆的直径 load = percent*2*raduis,这个数值放到 path.moveTo(cx-2*raduis +offsetX,cy+raduis-load);
//然后当下载完成后,取消动画
percent++;
if (percent==(2*raduis+(raduis/8))){
animator.cancel();
}
//贝塞尔曲线的起始点, 动态改变起点位置
path.moveTo(cx-2*raduis +offsetX,cy+raduis-percent);
//一个波峰一个波谷是一个波浪,
for (int i = 0; i < 3; i++) {
//一个波峰
path.rQuadTo(raduis/4 ,-raduis/8,raduis/2,0);
//一个波谷
path.rQuadTo(raduis/4,raduis/8,raduis/2,0);
}
//画出圆形
canvas.drawPath(cpath,cpaint);
//封闭path路径,
path.lineTo(getWidth(),getHeight());
path.lineTo(0,getHeight());
path.close();
path.op(cpath, Path.Op.INTERSECT);
canvas.drawPath(path,paint);
//先画一个矩形,来确定中心位置,让文字可以放大最中间
canvas.drawRect(rect, rectPaint);
canvas.drawText(percent+"",rect.centerX(),baseLineY,txtPaint);
}
}
网友评论