Android初学之自定义简单蛛网(雷达)图

因项目需要,需要加入蛛网图的显示,虽然GitHub上早已有很多大神的nb框架,但是还是想自己写写练练手。
项目中显示的蛛网图也比较简单,只需要控制四个进度值,这样就不需要考虑角度旋转的问题了 ,只需要控制上下左右四个进度值得变化就可以了,也就是只取上下左右四个进度值的点就可以了。
首先先获取各个重要点位信息:

@Override
    protected 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() {
            @Override
            public 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();
    }

    @Override
    protected 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);
    }

    @Override
    public 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() {
            @Override
            public 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();
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值