雷达图(蜘蛛网)的实现
2018-01-18 本文已影响0人
木小伍
废话不多说,先直接上最终的效果图(完整代码在底部)

实现思路
首先,我们将效果图实现的过程,拆分:
1,将6边形画出来
2,将对角线连接
3,在6个角上面添加上文字(我默认为右上角的为起始位置)
4,按照百分比,标出红点的位子,并连接,最后实现最终的效果
上面四点就是主要思路,现在思路有了就开始动手撸代码了。
private Paint mPaint = new Paint(); //画6边形的笔
private float mHeight, mWidth; //圆心
private float mRadius = 180;
private int count = 4; //蜘蛛网的层数为4
private Paint textPaint = new Paint(); //画文字的笔
private String[] titles = {"A", "B", "C", "D", "E", "F"}; //6个顶点的名称
private float[] pcent = {0.8f, 0.6f, 0.2f, 0.9f, 0.5f, 0.7f};
// private float[] pcent = {0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f};
private Paint shadePaint = new Paint(); //阴影图的画笔
这些是定义的成员变量,下面进入正题。
Step 1. 画6边形 (需要先将canvas的原点,移到屏幕中心)
//画6边形在ondraw方法中,
float x_off = 0; //层数递减,横坐标相对应的递减值
float y_off = 0; //层数递减,纵坐标相对应的递减值
// canvas.drawCircle(0, 0, mRadius, mPaint); //画出内切圆
for (int i = 0; i < count; i++) { //控制蜘蛛网的层数
x_off = (mRadius / 2) - (i * mRadius / count / 2);
y_off = (mRadius - (i * mRadius / count)) * (float) Math.sqrt(3) / 2;
for (int j = 0; j < 6; j++) { //画6边形
canvas.drawLine(-x_off,
y_off,
x_off,
y_off, mPaint);
canvas.rotate(60);
}
}
这段代码跑完之后就是这个图了


这里只要理解清楚了,每一层递减的时候,x坐标与y坐标的变化关系,就一切了然了。
强烈建议理解的时候,顺便画个三角形,然后里面画几道杠杠。通过一系列的sin,cos我们就可以得到以下的规律:
x轴递减规律为:每缩小一层,x轴的坐标,就减少 半径 除以 总共的层数 乘以 sin30(也就是二分之一)的长度。也就是上述代码 x_off代码表示计算方法。
y轴递减规律为:每缩小一层,y轴的坐标,就减少 半径 除以 总共的层数 乘以 cos30(也就是 二分之根号三)的长度。业绩宿舍上述代码 y_off 表示的计算方法。
因为只要画6边形,我们只需要每次画完一边,就旋转60度即可。
Step 2. 连接对角线
//将对角线连接
canvas.drawLine(-mRadius, 0, mRadius, 0, mPaint);
canvas.drawLine(-mRadius / 2, (float) Math.sqrt(3) * mRadius / 2,
mRadius / 2, -(float) Math.sqrt(3) * mRadius / 2, mPaint);
canvas.drawLine(-mRadius / 2, -(float) Math.sqrt(3) * mRadius / 2,
mRadius / 2, (float) Math.sqrt(3) * mRadius / 2, mPaint);
我们只关心,最外面的6个点的坐标,只要画出了6变形,那6个点的坐标就很容易得到了。效果图如下

Step 3.填加文字到6个点上面
//在6个角画上文字
canvas.drawText(titles[0], mRadius / 2, -(float) Math.sqrt(3) * mRadius / 2 - 5, textPaint);
canvas.drawText(titles[1], mRadius + 5, 0, textPaint);
canvas.drawText(titles[2], mRadius / 2, (float) Math.sqrt(3) * mRadius / 2 + 15, textPaint);
canvas.drawText(titles[3], -mRadius / 2, (float) Math.sqrt(3) * mRadius / 2 + 15, textPaint);
canvas.drawText(titles[4], -mRadius - 15, 0, textPaint);
canvas.drawText(titles[5], -mRadius / 2, -(float) Math.sqrt(3) * mRadius / 2 - 5, textPaint);
为了更美观,我并没有直接把文字画在6个点上,而是朝外面移动了部分距离,(呃....左边的”小周“要按照实际的情况再把横坐标朝向左边移动。这里仅仅作为展示效果,就不要在意这些细节了。。。)

Step 4.画阴影图了
//画阴影部分
ArrayList<PointF> pointFs = new ArrayList<>();
pointFs.add(new PointF(mRadius / 2, -(float) Math.sqrt(3) * mRadius / 2));
pointFs.add(new PointF(mRadius, 0));
pointFs.add(new PointF(mRadius / 2, (float) Math.sqrt(3) * mRadius / 2));
pointFs.add(new PointF(-mRadius / 2, (float) Math.sqrt(3) * mRadius / 2));
pointFs.add(new PointF(-mRadius, 0));
pointFs.add(new PointF(-mRadius / 2, -(float) Math.sqrt(3) * mRadius / 2));
Path path = new Path();
float x, y;
for (int i = 0; i < pointFs.size(); i++) { //获取百分比之后的点
x = pointFs.get(i).x * pcent[i];
y = pointFs.get(i).y * pcent[i];
if (i == 0) {
path.moveTo(x, y); //路线起点移动到第一个点的位置
} else {
path.lineTo(x, y);
}
canvas.drawCircle(x, y, 4, textPaint); //画出小红点
}
canvas.drawPath(path, shadePaint);
这个就是最终的效果图了。
全部代码如下(注意适配问题,需要将px转换成为dp):
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.os.Build;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.view.View;
import java.util.ArrayList;
/**
* Created by 伍跃武 on 2018/1/17.
* 雷达图
*/
public class SpiderView extends View {
public SpiderView(Context context) {
this(context, null);
}
public SpiderView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public SpiderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public SpiderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
private Paint mPaint = new Paint(); //画6边形的笔
private float mHeight, mWidth; //圆心
private float mRadius = 180;
private int count = 4; //蜘蛛网的层数为4
private Paint textPaint = new Paint(); //画文字的笔
private String[] titles = {"A", "B", "C", "D", "E", "F"}; //6个顶点的名称
private float[] pcent = {0.8f, 0.6f, 0.2f, 0.9f, 0.5f, 0.7f};
// private float[] pcent = {0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f};
private Paint shadePaint = new Paint(); //阴影图的画笔
/**
* 初始化视图
*/
private void initView() {
mPaint.setStrokeWidth(2f);
mPaint.setAntiAlias(true);
mPaint.setColor(Color.GREEN);
textPaint.setAntiAlias(true);
textPaint.setStrokeWidth(1f);
textPaint.setColor(Color.RED);
textPaint.setTextSize(16);
shadePaint.setColor(Color.GRAY);
shadePaint.setStyle(Paint.Style.FILL_AND_STROKE);
shadePaint.setAlpha(122);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mHeight = h / 2;
mWidth = w / 2;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.BLACK);
canvas.translate(mWidth, mHeight); //原点移到中心
mPaint.setStyle(Paint.Style.STROKE);//不填充颜色
canvas.drawCircle(0, 0, mRadius, mPaint);
// canvas.drawPoint(0,0,mPaint);
//画6边形
float x_off = 0; //层数递减,横坐标相对应的递减值
float y_off = 0; //层数递减,纵坐标相对应的递减值
for (int i = 0; i < count; i++) { //控制蜘蛛网的层数
x_off = (mRadius / 2) - (i * mRadius / count / 2);
y_off = (mRadius - (i * mRadius / count)) * (float) Math.sqrt(3) / 2;
for (int j = 0; j < 6; j++) { //画6边形
canvas.drawLine(-x_off,
y_off,
x_off,
y_off, mPaint);
canvas.rotate(60);
}
}
//将对角线连接
canvas.drawLine(-mRadius, 0, mRadius, 0, mPaint);
canvas.drawLine(-mRadius / 2, (float) Math.sqrt(3) * mRadius / 2,
mRadius / 2, -(float) Math.sqrt(3) * mRadius / 2, mPaint);
canvas.drawLine(-mRadius / 2, -(float) Math.sqrt(3) * mRadius / 2,
mRadius / 2, (float) Math.sqrt(3) * mRadius / 2, mPaint);
//在6个角画上文字
canvas.drawText(titles[0], mRadius / 2, -(float) Math.sqrt(3) * mRadius / 2 - 5, textPaint);
canvas.drawText(titles[1], mRadius + 5, 0, textPaint);
canvas.drawText(titles[2], mRadius / 2, (float) Math.sqrt(3) * mRadius / 2 + 15, textPaint);
canvas.drawText(titles[3], -mRadius / 2, (float) Math.sqrt(3) * mRadius / 2 + 15, textPaint);
canvas.drawText(titles[4], -mRadius - 15, 0, textPaint);
canvas.drawText(titles[5], -mRadius / 2, -(float) Math.sqrt(3) * mRadius / 2 - 5, textPaint);
//画阴影部分
ArrayList<PointF> pointFs = new ArrayList<>();
pointFs.add(new PointF(mRadius / 2, -(float) Math.sqrt(3) * mRadius / 2));
pointFs.add(new PointF(mRadius, 0));
pointFs.add(new PointF(mRadius / 2, (float) Math.sqrt(3) * mRadius / 2));
pointFs.add(new PointF(-mRadius / 2, (float) Math.sqrt(3) * mRadius / 2));
pointFs.add(new PointF(-mRadius, 0));
pointFs.add(new PointF(-mRadius / 2, -(float) Math.sqrt(3) * mRadius / 2));
Path path = new Path();
float x, y;
for (int i = 0; i < pointFs.size(); i++) { //获取百分比之后的点
x = pointFs.get(i).x * pcent[i];
y = pointFs.get(i).y * pcent[i];
if (i == 0) {
path.moveTo(x, y); //路线起点移动到第一个点的位置
} else {
path.lineTo(x, y);
}
canvas.drawCircle(x, y, 4, textPaint); //画出小红点
}
canvas.drawPath(path, shadePaint);
}
public void setTitles(String[] titles) {
this.titles = titles;
invalidate();
}
public void setPcent(float[] pcent) {
this.pcent = pcent;
invalidate();
}
}