因项目需要,需要加入蛛网图的显示,虽然GitHub上早已有很多大神的nb框架,但是还是想自己写写练练手。
项目中显示的蛛网图也比较简单,只需要控制四个进度值,这样就不需要考虑角度旋转的问题了 ,只需要控制上下左右四个进度值得变化就可以了,也就是只取上下左右四个进度值的点就可以了。
首先先获取各个重要点位信息:
@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);//初始化各边信息 获取各方向位置点起始位置信息left = leftRightPadding;top = topBottomPadding;right = w - leftRightPadding;bottom = h - topBottomPadding;if (centerPoint == null) {centerPoint = new PointF();}centerPoint.x = w / 2;centerPoint.y = h / 2;progressSpaceX = (centerPoint.x - left)/4;progressSpaceY = (centerPoint.y - top)/4;if (shader == null) {//初始化渐变色背景shader = new LinearGradient(centerPoint.x,top,centerPoint.x,bottom,Color.parseColor("#18C2B1"),Color.parseColor("#FDA23E"), Shader.TileMode.MIRROR);}setProgressValue();}
然后实现蛛网图的刻度效果,主要代码
//画横竖两条背景线path.moveTo(left,centerPoint.y);path.lineTo(right,centerPoint.y);paint.setStyle(Paint.Style.STROKE);paint.setColor(Color.parseColor("#cccccc"));paint.setStrokeWidth(3);canvas.drawPath(path,paint);path.reset();path.moveTo(centerPoint.x,top);path.lineTo(centerPoint.x,bottom);canvas.drawPath(path,paint);//画蛛网线paint.setShader(shader);paint.setStrokeWidth(3);for (int i = 0; i < 4; i++) {path.reset();path.moveTo(left + progressSpaceX * i,centerPoint.y);path.lineTo(centerPoint.x,top + progressSpaceY * i);path.lineTo(right - progressSpaceX * i,centerPoint.y);path.lineTo(centerPoint.x,bottom - progressSpaceY * i);path.lineTo(left + progressSpaceX * i,centerPoint.y);canvas.drawPath(path,paint);paint.setStrokeWidth(2);}
然后画个进度标题(示例代码写死,实际应用可自定义标题):
//画标题paint.setShader(null);paint.setColor(textColor);paint.setStyle(Paint.Style.FILL);paint.setTextSize(textSize);paint.setTextAlign(Paint.Align.CENTER);paint.getTextBounds("节能",0,2,rect);//计算文字高度 左右标题居中显示float de = rect.height();canvas.drawText("节能",centerPoint.x, topBottomPadding /2,paint);canvas.drawText("灾害",centerPoint.x,bottom + topBottomPadding /2,paint);canvas.drawText("卫生",leftRightPadding / 2,centerPoint.y + de/2,paint);canvas.drawText("效能",leftRightPadding / 2 + right,centerPoint.y + de/2,paint);
之后画进度值:
//画四个方向进度圆点paint.setColor(Color.parseColor("#00BDAA"));paint.setStyle(Paint.Style.FILL);canvas.drawCircle(topPoint.x,topPoint.y,pointCircleRadius,paint);canvas.drawCircle(leftPoint.x,leftPoint.y,pointCircleRadius,paint);canvas.drawCircle(rightPoint.x,rightPoint.y,pointCircleRadius,paint);canvas.drawCircle(bottomPoint.x,bottomPoint.y,pointCircleRadius,paint);//画四个方向进度区域paint.setStyle(Paint.Style.STROKE);paint.setStrokeWidth(5);path.reset();path.moveTo(leftPoint.x,leftPoint.y);path.lineTo(topPoint.x,topPoint.y);path.lineTo(rightPoint.x,rightPoint.y);path.lineTo(bottomPoint.x,bottomPoint.y);path.lineTo(leftPoint.x,leftPoint.y);canvas.drawPath(path,paint);paint.setStyle(Paint.Style.FILL);paint.setColor(Color.parseColor("#b355C7C2"));canvas.drawPath(path,paint);
最后加上动态效果:
/*** 启动动画* @param topProgress 上方进度值* @param rightProgress 右方进度值* @param bottomProgress 底部进度值* @param leftProgress 左方进度值*/private void startAnimator(final double topProgress,final double rightProgress,final double bottomProgress, final double leftProgress){ValueAnimator topAnimator = ValueAnimator.ofInt(0, 100);topAnimator.setDuration(2000);topAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {Integer v = (Integer) animation.getAnimatedValue();DayHealthProgressView.this.topProgress = v * topProgress/100 ;DayHealthProgressView.this.rightProgress = v* rightProgress/100 ;DayHealthProgressView.this.bottomProgress = v* bottomProgress/100 ;DayHealthProgressView.this.leftProgress = v* leftProgress/100 ;setProgressValue();invalidate();}});topAnimator.start();}
最终效果如下:
完整代码:
package com.zc.tt;import android.animation.ValueAnimator;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.LinearGradient;import android.graphics.Paint;import android.graphics.Path;import android.graphics.PointF;import android.graphics.Rect;import android.graphics.Shader;import android.support.annotation.Nullable;import android.util.AttributeSet;import android.view.View;import com.blankj.utilcode.util.ConvertUtils;/*** 自定义每日健康蛛网图* @author zc*/public class DayHealthProgressView extends View {/**画笔*/private Paint paint;private Path path;private Rect rect;/*** 控制四个点信息,中心点、上方进度值点、下方进度值点、左方进度值点、右方进度值点*/private PointF centerPoint,topPoint, bottomPoint,leftPoint,rightPoint;/**蛛网图各方向起始位置*/private float left,right,top,bottom;/**上下间距*/private float topBottomPadding;/**左右间距*/private float leftRightPadding;/**左右进度值单位长度、上下进度值单位长度*/private float progressSpaceX,progressSpaceY;/**背景shader*/private LinearGradient shader;/**字体大小*/private float textSize;/**字体颜色*/private int textColor;/**上方进度值*/private double topProgress;/**下方进度值*/private double bottomProgress;/**左方进度值*/private double leftProgress;/**右方进度值*/private double rightProgress;/**中心店圆的Radius*/private float pointCircleRadius;public DayHealthProgressView(Context context) {super(context);init();}public DayHealthProgressView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init();}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);//初始化各边信息 获取各方向位置点起始位置信息left = leftRightPadding;top = topBottomPadding;right = w - leftRightPadding;bottom = h - topBottomPadding;if (centerPoint == null) {centerPoint = new PointF();}centerPoint.x = w / 2;centerPoint.y = h / 2;progressSpaceX = (centerPoint.x - left)/4;progressSpaceY = (centerPoint.y - top)/4;if (shader == null) {//初始化渐变色背景shader = new LinearGradient(centerPoint.x,top,centerPoint.x,bottom,Color.parseColor("#18C2B1"),Color.parseColor("#FDA23E"), Shader.TileMode.MIRROR);}setProgressValue();}/*** 初始化*/private void init(){if (topPoint == null) {topPoint = new PointF();}if (leftPoint == null) {leftPoint = new PointF();}if (bottomPoint == null) {bottomPoint = new PointF();}if (rightPoint == null) {rightPoint = new PointF();}if (centerPoint == null) {centerPoint = new PointF();}paint = new Paint();path = new Path();paint.setAntiAlias(true);//上下间距和左右间距不一样 左右间距过小会导致文字信息显示不全topBottomPadding = ConvertUtils.dp2px(30);leftRightPadding = ConvertUtils.dp2px(36);textSize = ConvertUtils.sp2px(16);textColor = Color.parseColor("#333333");rect = new Rect();pointCircleRadius = ConvertUtils.dp2px(3);}@Overridepublic void draw(Canvas canvas) {super.draw(canvas);//画横竖两条线path.moveTo(left,centerPoint.y);path.lineTo(right,centerPoint.y);paint.setStyle(Paint.Style.STROKE);paint.setColor(Color.parseColor("#cccccc"));paint.setStrokeWidth(3);canvas.drawPath(path,paint);path.reset();path.moveTo(centerPoint.x,top);path.lineTo(centerPoint.x,bottom);canvas.drawPath(path,paint);//画蛛网线paint.setShader(shader);paint.setStrokeWidth(3);for (int i = 0; i < 4; i++) {path.reset();path.moveTo(left + progressSpaceX * i,centerPoint.y);path.lineTo(centerPoint.x,top + progressSpaceY * i);path.lineTo(right - progressSpaceX * i,centerPoint.y);path.lineTo(centerPoint.x,bottom - progressSpaceY * i);path.lineTo(left + progressSpaceX * i,centerPoint.y);canvas.drawPath(path,paint);paint.setStrokeWidth(2);}//画标题paint.setShader(null);paint.setColor(textColor);paint.setStyle(Paint.Style.FILL);paint.setTextSize(textSize);paint.setTextAlign(Paint.Align.CENTER);paint.getTextBounds("节能",0,2,rect);//计算文字高度 左右标题居中显示float de = rect.height();canvas.drawText("节能",centerPoint.x, topBottomPadding /2,paint);canvas.drawText("灾害",centerPoint.x,bottom + topBottomPadding /2,paint);canvas.drawText("卫生",leftRightPadding / 2,centerPoint.y + de/2,paint);canvas.drawText("效能",leftRightPadding / 2 + right,centerPoint.y + de/2,paint);//画四个方向进度圆点paint.setColor(Color.parseColor("#00BDAA"));paint.setStyle(Paint.Style.FILL);canvas.drawCircle(topPoint.x,topPoint.y,pointCircleRadius,paint);canvas.drawCircle(leftPoint.x,leftPoint.y,pointCircleRadius,paint);canvas.drawCircle(rightPoint.x,rightPoint.y,pointCircleRadius,paint);canvas.drawCircle(bottomPoint.x,bottomPoint.y,pointCircleRadius,paint);//画四个方向进度区域paint.setStyle(Paint.Style.STROKE);paint.setStrokeWidth(5);path.reset();path.moveTo(leftPoint.x,leftPoint.y);path.lineTo(topPoint.x,topPoint.y);path.lineTo(rightPoint.x,rightPoint.y);path.lineTo(bottomPoint.x,bottomPoint.y);path.lineTo(leftPoint.x,leftPoint.y);canvas.drawPath(path,paint);paint.setStyle(Paint.Style.FILL);paint.setColor(Color.parseColor("#b355C7C2"));canvas.drawPath(path,paint);}/*** 确定进度圆点值*/private void setProgressValue() {if (topPoint == null) {topPoint = new PointF();}if (leftPoint == null) {leftPoint = new PointF();}if (bottomPoint == null) {bottomPoint = new PointF();}if (rightPoint == null) {rightPoint = new PointF();}topPoint.y = (float) (((centerPoint.y - top) * (100 - topProgress)) / 100 + top);topPoint.x = centerPoint.x;bottomPoint.y = (float) ((bottomProgress * (centerPoint.y - topBottomPadding))/100 + centerPoint.y);bottomPoint.x = centerPoint.x;leftPoint.x = (float) (((centerPoint.x - left) * (100 - leftProgress)) / 100 + left);leftPoint.y = centerPoint.y;rightPoint.x = (float) ((rightProgress * (centerPoint.x - leftRightPadding))/100 + centerPoint.x);rightPoint.y = centerPoint.y;}public void setProgress(double topProgress,double rightProgress,double bottomProgress,double leftProgress){startAnimator(topProgress,rightProgress,bottomProgress,leftProgress);}/*** 启动动画* @param topProgress 上方进度值* @param rightProgress 右方进度值* @param bottomProgress 底部进度值* @param leftProgress 左方进度值*/private void startAnimator(final double topProgress,final double rightProgress,final double bottomProgress, final double leftProgress){ValueAnimator topAnimator = ValueAnimator.ofInt(0, 100);topAnimator.setDuration(2000);topAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {Integer v = (Integer) animation.getAnimatedValue();DayHealthProgressView.this.topProgress = v * topProgress/100 ;DayHealthProgressView.this.rightProgress = v* rightProgress/100 ;DayHealthProgressView.this.bottomProgress = v* bottomProgress/100 ;DayHealthProgressView.this.leftProgress = v* leftProgress/100 ;setProgressValue();invalidate();}});topAnimator.start();}public double getTopProgress() {return topProgress;}public void setTopProgress(double topProgress) {this.topProgress = topProgress;setProgressValue();invalidate();}public double getBottomProgress() {return bottomProgress;}public void setBottomProgress(double bottomProgress) {this.bottomProgress = bottomProgress;setProgressValue();invalidate();}public double getLeftProgress() {return leftProgress;}public void setLeftProgress(double leftProgress) {this.leftProgress = leftProgress;setProgressValue();invalidate();}public double getRightProgress() {return rightProgress;}public void setRightProgress(double rightProgress) {this.rightProgress = rightProgress;setProgressValue();invalidate();}public float getPointCircleRadius() {return pointCircleRadius;}public void setPointCircleRadius(float pointCircleRadius) {this.pointCircleRadius = pointCircleRadius;invalidate();}}