Android自定义View——雷达图

Java代码
public class EvaluationView extends View {
    private int mNormalColor;  //默认颜色
    private int mHighlightColor;  //高亮颜色
    private int mLineCount;  //n边型
    private int mGeometryCount;  //n边型的个数
    private float mNormalStrokeWidth;  //默认线的宽度
    private float mHighlightStrokeWidth;  //高亮线的宽度
    private boolean mhighlightFill; //高亮部分是否是填充模式
    private boolean needUpWords;  //默认是从x轴正方向开始画,但是这样画出来的结果不一样尖尖朝上,这个属性就表示是否需要尖尖朝上,如果需要尖尖朝上那么会旋转-90度
    private List<String> mTitles;
    private List<Integer> levels;

    private Paint mPaint;
    private TextPaint mTextPaint;
    private Path mPath;
    private float mCenterX;
    private float mCenterY;
    private float mMaxRadius;  //最外层的多边形的半径
    private float mAngle;
    private int space;

    public EvaluationView(Context context) {
        this(context, null);
    }

    public EvaluationView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public EvaluationView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.EvaluationView);
        mNormalColor = array.getColor(R.styleable.EvaluationView_normalColor, Color.GRAY);
        mHighlightColor = array.getColor(R.styleable.EvaluationView_highlightColor, Color.parseColor("#500000ff"));
        int textColor = array.getColor(R.styleable.EvaluationView_android_textColor, Color.BLACK);
        float textSize = array.getDimension(R.styleable.EvaluationView_android_textSize, AppUtils.sp2px(15, getResources().getDisplayMetrics()));
        mLineCount = array.getInt(R.styleable.EvaluationView_lineCount, 5);
        mGeometryCount = array.getInt(R.styleable.EvaluationView_geometryCount, 5);
        mNormalStrokeWidth = array.getDimension(R.styleable.EvaluationView_normalStrokeWidth, AppUtils.dp2px(1, getResources().getDisplayMetrics()));
        mHighlightStrokeWidth = array.getDimension(R.styleable.EvaluationView_highlightStrokeWidth, AppUtils.dp2px(5, getResources().getDisplayMetrics()));
        mhighlightFill = array.getBoolean(R.styleable.EvaluationView_isHighlightFill, true);
        needUpWords = array.getBoolean(R.styleable.EvaluationView_needUpwards, true);
        array.recycle();
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setAntiAlias(true);
        mTextPaint = new TextPaint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setTextSize(textSize);
        mTextPaint.setColor(textColor);
        mPath = new Path();
        mAngle = (float) (2 * Math.PI / mLineCount);
        initTitles();
        space = AppUtils.dp2px(4, getResources().getDisplayMetrics());
        initLevels();
    }

    private void initTitles() {
        mTitles = new ArrayList<>();
        for (int i = 0; i < mLineCount; i++) {
            mTitles.add("第" + i + "个");
        }
    }

    private void initLevels() {
        levels = new ArrayList<>();
        Random random = new Random();
        for (int i = 0; i < mLineCount; i++) {
            int e = random.nextInt(mGeometryCount + 1);
            if (e == 0) {
                e = 1;
            }
            levels.add(e);
        }
    }

    public void setTitles(List<String> titles) {
        if (titles != null && titles.size() > 0) {
            mTitles = titles;
            postInvalidate();
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        //先平移到中心点
        canvas.translate(mCenterX, mCenterY);
        if (needUpWords) {  //如果尖尖需要朝上,则需要旋转-90度
            canvas.save();
            canvas.rotate(-90);
        }

        drawPloygon(canvas);

        if (!needUpWords) {
            drawText(canvas);
        }

        drawHighlight(canvas);

        if (needUpWords) {
            canvas.restore();
            drawableUpWordsText(canvas);
        }
    }


    /**
     * 绘制多边形
     * 顺时针绘制
     *
     * @param canvas
     */
    private void drawPloygon(Canvas canvas) {
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(mNormalColor);
        mPaint.setStrokeWidth(mNormalStrokeWidth);
        for (int i = 0; i < mGeometryCount; i++) { //绘制n个多边形  从外往里面画
            float radius = mMaxRadius - i * mMaxRadius / mGeometryCount;  //每个多边形的半径
            mPath.reset();
            for (int j = 0; j < mLineCount; j++) {
                float x = (float) (Math.cos(j * mAngle) * radius);
                float y = (float) (Math.sin(j * mAngle) * radius);
                if (j == 0) {
                    mPath.moveTo(x, y);
                } else {
                    mPath.lineTo(x, y);
                }
                if (i == 0) {
                    canvas.drawLine(0, 0, x, y, mPaint);
                }
            }
            mPath.close();
            canvas.drawPath(mPath, mPaint);
        }
    }

    /**
     * 绘制文字,从x轴正方向开始绘制,顺时针
     *
     * @param canvas
     */
    private void drawText(Canvas canvas) {
        Rect rect = new Rect();
        //绘制文字
        for (int i = 0; i < mTitles.size(); i++) {
            String title = mTitles.get(i);
            mTextPaint.getTextBounds(title, 0, title.length(), rect);
            float textWidth = rect.width();
            float textHeight = rect.height();

            float x = (float) (Math.cos(i * mAngle) * mMaxRadius);
            float y = (float) (Math.sin(i * mAngle) * mMaxRadius);
            if (x <= 0) {
                x = x - textWidth - space;
            } else {
                x = x + space;
            }
            if (y > 0) {
                y = y + textHeight;
            } else {
                y = y - space;
            }
            canvas.drawText(title, x, y, mTextPaint);
        }
    }

    /**
     * 绘制高亮部分
     *
     * @param canvas
     */
    private void drawHighlight(Canvas canvas) {
        //绘制高亮布局
        if (mhighlightFill) {
            mPaint.setStyle(Paint.Style.FILL);
        } else {
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setStrokeWidth(mHighlightStrokeWidth);
        }
        mPaint.setColor(mHighlightColor);
        mPath.reset();
        for (int i = 0; i < levels.size(); i++) {
            Integer level = levels.get(i);
            float x = (float) (Math.cos(i * mAngle) * mMaxRadius * level / mGeometryCount);
            float y = (float) (Math.sin(i * mAngle) * mMaxRadius * level / mGeometryCount);
            if (i == 0) {
                mPath.moveTo(x, y);
            } else {
                mPath.lineTo(x, y);
            }
        }
        mPath.close();
        canvas.drawPath(mPath, mPaint);
    }

    /**
     * 绘制需要尖尖朝上的文字,因为旋转了-90度,所以从最上面开始绘制,顺时针
     *
     * @param canvas
     */
    private void drawableUpWordsText(Canvas canvas) {
        int size = mTitles.size();
        String title;
        Rect rect = new Rect();
        float textWidth;
        float textHeight;

        for (int i = 0; i < size; i++) {
            title = mTitles.get(i);
            mTextPaint.getTextBounds(title, 0, title.length(), rect);
            textWidth = rect.width();
            textHeight = rect.height();
            if (i == 0) {
                canvas.drawText(title, 0 - textWidth / 2, 0 - mMaxRadius - space, mTextPaint);
            } else {
                float x = (float) (mMaxRadius * Math.sin(i * mAngle));
                float y = (float) (-mMaxRadius * Math.cos(i * mAngle));
                if ((int) (Math.abs(x)) <= mMaxRadius * Math.sin(mAngle / 2)) {
                    x = x - textWidth / 2;
                } else {
                    if (x <= 0) {
                        x = x - textWidth - space;
                    } else {
                        x = x + space;
                    }
                }
                y = y + textHeight / 2;
                if (y >= mMaxRadius * Math.cos(mAngle / 2)) {
                    y += textHeight / 2 + space;
                }
                canvas.drawText(title, x, y, mTextPaint);
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        if (widthMode != MeasureSpec.EXACTLY) {
            //如果宽度不是确切的值,那么给一个默认值500dp
            width = AppUtils.dp2px(500, getResources().getDisplayMetrics());
        }
        if (heightMode != MeasureSpec.EXACTLY) {
            //如果高度不是确切的值,给一个默认值200dp
            height = AppUtils.dp2px(200, getResources().getDisplayMetrics());
        }
        setMeasuredDimension(width, height);
        mCenterX = width / 2;
        mCenterY = height / 2;
        //最外层多边形的半径是取宽高的最小值 - 50dp后再除以2  ,为什么-50dp原因在于要预留出文字的区域,但50dp也可能不够,所以宽高要设好
        mMaxRadius = (Math.min(width, height) - AppUtils.dp2px(50, getResources().getDisplayMetrics())) / 2;
    }
}
Style属性
 <declare-styleable name="EvaluationView">
        <attr name="normalColor" format="color" />
        <attr name="highlightColor" format="color" />
        <attr name="isHighlightFill" format="boolean" />
        <attr name="android:textColor" />
        <attr name="android:textSize" />
        <attr name="lineCount" format="integer" />
        <attr name="geometryCount" format="integer" />
        <attr name="normalStrokeWidth" format="dimension" />
        <attr name="highlightStrokeWidth" format="dimension" />
        <attr name="needUpwards" format="boolean" />
    </declare-styleable>
使用
<com.star.testapplication.views.EvaluationView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_margin="20dp"
            android:visibility="visible"
            app:geometryCount="4"
            app:highlightStrokeWidth="2dp"
            app:isHighlightFill="true"
            app:lineCount="6"
            app:needUpwards="false" />
效果

这里写图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值