Android基础自定义View--雷达图

本文主要参考文章:http://blog.csdn.net/crazy__chen/article/details/50163693
当然还有:鸿洋大神的博客。
雷达图:

类似下图,能表示一个人的综合能力分布。
这里写图片描述

自定义View:

自定义属性:

新建attr.xml
包括总数 颜色 数值 文字

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="PolygonColor" format="color" />
    <attr name="RegionColor" format="color" />
    <attr name="TextColor" format="color" />
    <attr name="count" format="integer" />
    <attr name="title1" format="string" />
    <attr name="title2" format="string" />
    <attr name="title3" format="string" />
    <attr name="title4" format="string" />
    <attr name="title5" format="string" />
    <attr name="title6" format="string" />
    <attr name="data1" format="integer" />
    <attr name="data2" format="integer" />
    <attr name="data3" format="integer" />
    <attr name="data4" format="integer" />
    <attr name="data5" format="integer" />
    <attr name="data6" format="integer" />
    <declare-styleable name="RadarView">
        <attr name="PolygonColor" />
        <attr name="RegionColor" />
        <attr name="TextColor" />
        <attr name="count" />
        <attr name="title1" />
        <attr name="title2" />
        <attr name="title3" />
        <attr name="title4" />
        <attr name="title5" />
        <attr name="title6" />
        <attr name="data1" />
        <attr name="data2" />
        <attr name="data3" />
        <attr name="data4" />
        <attr name="data5" />
        <attr name="data6" />
    </declare-styleable>
</resources>

新建类继承View,构造函数

这里要设置变量以及初始化变量:

private int count;                //数据个数
    private int PolygonColor;
    private int TextColor;
    private int RegionColor;

    private float angle;
    private float radius;                   //网格最大半径
    private int centerX;                  //中心X
    private int centerY;                  //中心Y
    private List<String> titles = new ArrayList<String>();
    private List<Integer> data = new ArrayList<Integer>(); //各维度分值
    private float maxValue = 100;             //数据最大值
    private Paint mainPaint;                //雷达区画笔
    private Paint valuePaint;               //数据区画笔
    private Paint textPaint;                //文本画笔


    public RadarView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

    /**
     * 必要的初始化,获得一些自定义的值
     *
     * @param context
     * @param attrs
     * @param defStyle
     */
    public RadarView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.RadarView, defStyle, 0);
        int n = a.getIndexCount();
        for (int i = 0; i < n; i++) {
            int attr = a.getIndex(i);
            switch (attr) {
                case R.styleable.RadarView_count:
                    count = a.getInt(attr, 6);
                    break;
                case R.styleable.RadarView_PolygonColor:
                    PolygonColor = a.getColor(attr, Color.BLACK);
                    break;
                case R.styleable.RadarView_RegionColor:
                    RegionColor = a.getColor(attr, Color.BLUE);
                    break;
                case R.styleable.RadarView_TextColor:
                    TextColor = a.getColor(attr, Color.BLACK);
                    break;
                case R.styleable.RadarView_title1:
                    titles.add(a.getString(attr));
                    break;
                case R.styleable.RadarView_title2:
                    titles.add(a.getString(attr));
                    break;
                case R.styleable.RadarView_title3:
                    titles.add(a.getString(attr));
                    break;
                case R.styleable.RadarView_title4:
                    titles.add(a.getString(attr));
                    break;
                case R.styleable.RadarView_title5:
                    titles.add(a.getString(attr));
                    break;
                case R.styleable.RadarView_title6:
                    titles.add(a.getString(attr));
                    break;
                case R.styleable.RadarView_data1:
                    data.add(a.getInt(attr,100));
                    break;
                case R.styleable.RadarView_data2:
                    data.add(a.getInt(attr,100));
                    break;
                case R.styleable.RadarView_data3:
                    data.add(a.getInt(attr,100));
                    break;
                case R.styleable.RadarView_data4:
                    data.add(a.getInt(attr,100));
                    break;
                case R.styleable.RadarView_data5:
                    data.add(a.getInt(attr,100));
                    break;
                case R.styleable.RadarView_data6:
                    data.add(a.getInt(attr,100));
                    break;
            }
        }
        Log.d("data", data.toString());
        a.recycle();
        mainPaint = new Paint();
        mainPaint.setAntiAlias(true);
        mainPaint.setColor(PolygonColor);
        mainPaint.setStrokeWidth(3);
        mainPaint.setStyle(Paint.Style.STROKE);

        valuePaint = new Paint();
        valuePaint.setAntiAlias(true);
        valuePaint.setColor(RegionColor);
        valuePaint.setStyle(Paint.Style.FILL_AND_STROKE);

        textPaint = new Paint();
        textPaint.setTextSize(100);
        textPaint.setStyle(Paint.Style.FILL);
        textPaint.setColor(TextColor);

        angle = (float) (Math.PI * 2 / count);
    }

获取View中心点:

onMeasure函数之后,view会大小变化。由之前的0长和0宽变成现在的宽高。同时会调用onSizeChanged()函数。

//onSizeChanged方法 view的大小发生改变是被系统调用
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        Log.d("onSizeChanged", "新宽" + w + "新高" + h + "旧宽" + oldw + "旧高" + oldh + "");
        radius = Math.min(h, w) / 2 * 0.9f;
        centerX = w / 2;
        centerY = h / 2;
        //postInvalidate();
        super.onSizeChanged(w, h, oldw, oldh);
    }

开始画图:

重写onDraw函数:

@Override
    protected void onDraw(Canvas canvas) {
        Log.d("onDraw", "onDraw");
        drawPolygon(canvas);
        drawLines(canvas);
        drawText(canvas);
        drawRegion(canvas);
    }

    /**
     * 绘制正多边形
     */
    private void drawPolygon(Canvas canvas) {
        Log.d("drawPolygon", "drawPolygon");
        Path path = new Path();
        float r = radius / (count - 1);//r是蜘蛛丝之间的间距
        for (int i = 1; i < count; i++) {//中心点不用绘制
            float curR = r * i;//当前半径
            path.reset();
            for (int j = 0; j < count; j++) {
                if (j == 0) {
                    path.moveTo(centerX + curR, centerY);
                } else {
                    //根据半径,计算出蜘蛛丝上每个点的坐标
                    float x = (float) (centerX + curR * Math.cos(angle * j));
                    float y = (float) (centerY + curR * Math.sin(angle * j));
                    path.lineTo(x, y);
                }
            }
            path.close();//闭合路径
            canvas.drawPath(path, mainPaint);
        }
    }

    /**
     * 绘制直线
     */
    private void drawLines(Canvas canvas) {
        Path path = new Path();
        for (int i = 0; i < count; i++) {
            path.reset();
            path.moveTo(centerX, centerY);
            float x = (float) (centerX + radius * Math.cos(angle * i));
            float y = (float) (centerY + radius * Math.sin(angle * i));
            path.lineTo(x, y);
            canvas.drawPath(path, mainPaint);
        }
    }

    /**
     * 绘制文字
     *
     * @param canvas
     */
    private void drawText(Canvas canvas) {
        Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
        float fontHeight = fontMetrics.descent - fontMetrics.ascent;
        for (int i = 0; i < count; i++) {
            float x = (float) (centerX + (radius + fontHeight / 2) * Math.cos(angle * i));
            float y = (float) (centerY + (radius + fontHeight / 2) * Math.sin(angle * i));
            if (angle * i >= 0 && angle * i <= Math.PI / 2) {//第4象限
                canvas.drawText(titles.get(i), x, y, textPaint);
            } else if (angle * i >= 3 * Math.PI / 2 && angle * i <= Math.PI * 2) {//第3象限
                canvas.drawText(titles.get(i), x, y, textPaint);
            } else if (angle * i > Math.PI / 2 && angle * i <= Math.PI) {//第2象限
                float dis = textPaint.measureText(titles.get(i));//文本长度
                canvas.drawText(titles.get(i), x - dis, y, textPaint);
            } else if (angle * i >= Math.PI && angle * i < 3 * Math.PI / 2) {//第1象限
                float dis = textPaint.measureText(titles.get(i));//文本长度
                canvas.drawText(titles.get(i), x - dis, y, textPaint);
            }
        }
    }

    /**
     * 绘制区域
     *
     * @param canvas
     */
    private void drawRegion(Canvas canvas) {
        Path path = new Path();
        valuePaint.setAlpha(255);
        for (int i = 0; i < count; i++) {
            double percent = data.get(i) / maxValue;
            float x = (float) (centerX + radius * Math.cos(angle * i) * percent);
            float y = (float) (centerY + radius * Math.sin(angle * i) * percent);
            if (i == 0) {
                path.moveTo(x, centerY);
            } else {
                path.lineTo(x, y);
            }
            //绘制小圆点
            canvas.drawCircle(x, y, 10, valuePaint);
        }
        valuePaint.setStyle(Paint.Style.STROKE);
        canvas.drawPath(path, valuePaint);
        valuePaint.setAlpha(127);
        //绘制填充区域
        valuePaint.setStyle(Paint.Style.FILL_AND_STROKE);
        canvas.drawPath(path, valuePaint);
    }

调用测试:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:ww="http://schemas.android.com/apk/res-auto"
    tools:context="com.example.administrator.radarview.MainActivity">

    <com.example.administrator.radarview.RadarView
        android:layout_centerInParent="true"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        ww:count="6"
        ww:PolygonColor="@color/colorAccent"
        ww:TextColor="#00ff00"
        ww:RegionColor="#cc0cc0"
        ww:title1="ceshi1"
        ww:title2="ceshi2"
        ww:title3="ceshi3"
        ww:title4="ceshi4"
        ww:title5="ceshi5"
        ww:title6="ceshi6"
        ww:data1="20"
        ww:data2="30"
        ww:data3="0"
        ww:data4="100"
        ww:data5="70"
        ww:data6="50" />
</RelativeLayout>

效果:
这里写图片描述

发布了85 篇原创文章 · 获赞 40 · 访问量 24万+

猜你喜欢

转载自blog.csdn.net/lw_zhaoritian/article/details/52534960