大概效果这样
代码如下:
/**
* Create by Mazhanzhu on 2020/10/12
* LOL英雄战斗力效果
*/
public class LOLview extends View {
public static final String TAG = "LOLview";
private int n; //边的数量或者能力的个数
private float R; //最外圈的半径,顶点到中心点的距离
private int intervalCount; //间隔数量,就把半径分为几段
private float angle; //两条顶点到中线点的线之间的角度
private Paint linePaint; //画线的笔
private Paint textPaint; //画文字的笔
private int viewHeight; //控件宽度
private int viewWidth; //控件高度
private ArrayList<AbilityBean> data = new ArrayList<>(); //元数据
private ArrayList<ArrayList<PointF>> pointsArrayList = new ArrayList<>(); //存储多边形顶点数组的数组
private ArrayList<PointF> abilityPoints = new ArrayList<>(); //存储能力点的数组
public LOLview(Context context) {
//这地方改为this,使得不管怎么初始化都会进入第三个构造函数中
this(context, null);
}
public LOLview(Context context, @Nullable AttributeSet attrs) {
//这地方改为this,使得不管怎么初始化都会进入第三个构造函数中
this(context, attrs, 0);
}
public LOLview(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initSize(context);
initPaint(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//设置控件的最终视图大小(宽高)
setMeasuredDimension(viewWidth, viewHeight);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
initSize(getContext());
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (data.size() > 0) {
//把画布的原点移动到控件的中心点
canvas.translate(viewWidth / 2, viewHeight / 2);
//绘制多边形边,每一圈都绘制
drawPolygon(canvas);
/**
* 画轮廓线
* 1.先画最外面的多边形轮廓
* 2.再画顶点到中心的线
*/
drawOutLine(canvas);
//画能力线
drawAbilityLine(canvas);
//画能力描述的文字
drawAbilityText(canvas);
}
}
private void drawAbilityText(Canvas canvas) {
canvas.save();
//先计算出坐标来
ArrayList<PointF> textPoints = new ArrayList<>();
for (int i = 0; i < data.size(); i++) {
float r = R + PxUtils.dp2pxF(getContext(), 15f);
float x = (float) (r * Math.cos(i * angle - Math.PI / 2));
float y = (float) (r * Math.sin(i * angle - Math.PI / 2));
textPoints.add(new PointF(x, y));
}
//拿到字体测量器
Paint.FontMetrics metrics = textPaint.getFontMetrics();
for (int i = 0; i < data.size(); i++) {
float x = textPoints.get(i).x;
//ascent:上坡度,是文字的基线到文字的最高处的距离
//descent:下坡度,,文字的基线到文字的最低处的距离
float y = textPoints.get(i).y - (metrics.ascent + metrics.descent) / 2;
canvas.drawText(data.get(i).name, x, y, textPaint);
}
canvas.restore();
}
private void drawAbilityLine(Canvas canvas) {
canvas.save();
//先把能力点初始化出来
abilityPoints = new ArrayList<>();
for (int i = 0; i < data.size(); i++) {
float r = R * (data.get(i).num / 100.0f); //能力值/100再乘以半径就是所占的比例
float x = (float) (r * Math.cos(i * angle - Math.PI / 2));
float y = (float) (r * Math.sin(i * angle - Math.PI / 2));
abilityPoints.add(new PointF(x, y));
}
linePaint.setStrokeWidth(PxUtils.dp2px(getContext(), 2f));
// linePaint.setColor(Color.parseColor("#E96153"));
linePaint.setColor(Color.parseColor("#A67EA4E3"));
// linePaint.setStyle(Paint.Style.STROKE); //设置空心的
linePaint.setStyle(Paint.Style.FILL); //设置实心的
Path path = new Path(); //路径
for (int i = 0; i < data.size(); i++) {
float x = abilityPoints.get(i).x;
float y = abilityPoints.get(i).y;
if (i == 0) {
path.moveTo(x, y);
} else {
path.lineTo(x, y);
}
}
path.close(); //别忘了闭合
canvas.drawPath(path, linePaint);
canvas.restore();
}
private void drawOutLine(Canvas canvas) {
canvas.save();//保存画布当前状态(平移、放缩、旋转、裁剪等),和canvas.restore()配合使用
linePaint.setColor(Color.parseColor("#eef2fb"));
linePaint.setStyle(Paint.Style.STROKE); //设置空心的
//先画最外面的多边形轮廓
Path path = new Path(); //路径
for (int i = 0; i < data.size(); i++) {
//只需要第一组的点
float x = pointsArrayList.get(0).get(i).x;
float y = pointsArrayList.get(0).get(i).y;
if (i == 0) {
//如果是第一个点就把path的起点设置为这个点
path.moveTo(x, y);
} else {
path.lineTo(x, y);
}
}
path.close(); //闭合路径
canvas.drawPath(path, linePaint);
//再画顶点到中心的线
for (int i = 0; i < data.size(); i++) {
float x = pointsArrayList.get(0).get(i).x;
float y = pointsArrayList.get(0).get(i).y;
canvas.drawLine(0, 0, x, y, linePaint); //起点都是中心点
}
canvas.restore();
}
private void drawPolygon(Canvas canvas) {
canvas.save();//保存画布当前状态(平移、放缩、旋转、裁剪等),和canvas.restore()配合使用
linePaint.setStyle(Paint.Style.FILL_AND_STROKE); //设置为填充且描边
Path path = new Path(); //路径
for (int i = 0; i < intervalCount; i++) { //循环、一层一层的绘制
//每一层的颜色都都不同
switch (i) {
case 0:
linePaint.setColor(Color.parseColor("#eef2fb"));
break;
case 1:
linePaint.setColor(Color.parseColor("#dfe6f9"));
break;
case 2:
linePaint.setColor(Color.parseColor("#ccdaf5"));
break;
case 3:
linePaint.setColor(Color.parseColor("#b8c9f5"));
break;
}
for (int j = 0; j < data.size(); j++) { //每一层有n个点
float x = pointsArrayList.get(i).get(j).x;
float y = pointsArrayList.get(i).get(j).y;
if (j == 0) {
//如果是每层的第一个点就把path的起点设置为这个点
path.moveTo(x, y);
} else {
path.lineTo(x, y);
}
}
path.close(); //设置为闭合的
canvas.drawPath(path, linePaint);
path.reset(); //清除path存储的路径
}
canvas.restore();
}
/**
* 初始化一些固定数据
*
* @param context
*/
private void initSize(Context context) {
// R = dp2pxF(context, 100); //半径暂时设为100dp
intervalCount = 4; //有四层
angle = (float) ((2 * Math.PI) / data.size()); //一周是2π,这里用π,因为进制的问题,不能用360度,画出来会有问题
//拿到屏幕的宽高,单位是像素
int screenWidth = PxUtils.getScreenWidth(context) - PxUtils.dp2px(52);
//控件设置为正方向
viewWidth = screenWidth;
viewHeight = screenWidth / 3 * 2;
R = viewWidth / 4; //半径设为宽度的三分之一
}
/**
* 初始化画笔
*/
private void initPaint(Context context) {
//画线的笔
linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
//设置线宽度
linePaint.setStrokeWidth(PxUtils.dp2px(context, 1f));
//画文字的笔
textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
textPaint.setTextAlign(Paint.Align.CENTER); //设置文字居中
textPaint.setColor(Color.BLACK);
textPaint.setTextSize(PxUtils.sp2pxF(context, 14f));
}
/**
* 传入元数据
*/
public void setData(ArrayList<AbilityBean> list, Context context) {
if (list.size() == 0) {
return;
}
data = list;
initSize(context);
initPoints();
//View本身调用迫使view重画
invalidate();
}
/**
* 初始化多边形的所有点 每一圈7个点,有4圈
*/
private void initPoints() {
float x;
float y;
for (int i = 0; i < intervalCount; i++) {
//创建一个存储点的数组
ArrayList<PointF> points = new ArrayList<>();
for (int j = 0; j < data.size(); j++) {
float r = R * ((float) (4 - i) / intervalCount); //每一圈的半径都按比例减少
//这里减去Math.PI / 2 是为了让多边形逆时针旋转90度,所以后面的所有用到cos,sin的都要减
x = (float) (r * Math.cos(j * angle - Math.PI / 2));
y = (float) (r * Math.sin(j * angle - Math.PI / 2));
points.add(new PointF(x, y));
}
pointsArrayList.add(points);
}
}
}
怎么使用呢?使用如下:【这是我代码里面这样使用的】
其中用到了一个Bean类:
/**
* 今年居然不发年终奖,哎
*/
public class AbilityBean {
public String name = "";//能力的名称
public int num;//能力的值,范围0~100,单位%
public int id;//能力的ID
public AbilityBean(String name, int num, int id) {
this.name = name;
this.num = num;
this.id = id;
}
public AbilityBean() {
}
}
应该就可以直接使用了