android 自定义View 雷达
时间: 2023-08-31 15:06:24 浏览: 205
Android自定义View雷达图,也称为蜘蛛网图或者星型图,是一种很常见的数据可视化方式。在这种图中,多个数据维度会以不同的角度展示,而每个维度的数据则会以不同的长度表示。这样一来,我们就可以通过一个图形快速地了解多个数据维度的情况。下面是一个简单的实现。
首先,我们需要在 XML 中定义自定义 View 的布局:
```
<com.example.radarview.RadarView
android:id="@+id/radar_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp" />
```
接着,在 Java 代码中实现 View 的绘制逻辑:
```
public class RadarView extends View {
private int mCount = 6; // 雷达图维度
private float mRadius; // 雷达图半径
private float mAngle; // 雷达图每个维度的角度
private Paint mRadarPaint; // 雷达图画笔
private Paint mValuePaint; // 数据画笔
private String[] mTitles = {"A", "B", "C", "D", "E", "F"}; // 维度名称
private double[] mValues = {5, 4, 3, 2, 5, 1}; // 数据值
public RadarView(Context context) {
this(context, null);
}
public RadarView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public RadarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
// 初始化雷达图画笔
mRadarPaint = new Paint();
mRadarPaint.setStyle(Paint.Style.STROKE);
// 初始化数据画笔
mValuePaint = new Paint();
mValuePaint.setStyle(Paint.Style.FILL_AND_STROKE);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int size = Math.min(width, height);
mRadius = size / 2f * 0.8f;
mAngle = (float) (Math.PI * 2 / mCount);
setMeasuredDimension(size, size);
}
@Override
protected void onDraw(Canvas canvas) {
// 绘制雷达图
drawRadar(canvas);
// 绘制数据区域
drawValue(canvas);
}
private void drawRadar(Canvas canvas) {
Path path = new Path();
float r = mRadius / (mCount - 1); // 计算多边形边长
for (int i = 0; i < mCount; i++) {
float currentR = r * i + r; // 计算当前多边形的半径
path.reset();
for (int j = 0; j < mCount; j++) {
if (j == 0) {
path.moveTo(getMeasuredWidth() / 2f + currentR, getMeasuredHeight() / 2f);
} else {
float x = (float) (getMeasuredWidth() / 2f + currentR * Math.cos(mAngle * j));
float y = (float) (getMeasuredHeight() / 2f + currentR * Math.sin(mAngle * j));
path.lineTo(x, y);
}
}
path.close(); // 闭合路径
canvas.drawPath(path, mRadarPaint);
}
// 绘制连接线
for (int i = 0; i < mCount; i++) {
float x = (float) (getMeasuredWidth() / 2f + mRadius * Math.cos(mAngle * i));
float y = (float) (getMeasuredHeight() / 2f + mRadius * Math.sin(mAngle * i));
canvas.drawLine(getMeasuredWidth() / 2f, getMeasuredHeight() / 2f, x, y, mRadarPaint);
}
// 绘制维度名称
for (int i = 0; i < mCount; i++) {
float x = (float) (getMeasuredWidth() / 2f + (mRadius + 20) * Math.cos(mAngle * i));
float y = (float) (getMeasuredHeight() / 2f + (mRadius + 20) * Math.sin(mAngle * i));
canvas.drawText(mTitles[i], x, y, mValuePaint);
}
}
private void drawValue(Canvas canvas) {
Path path = new Path();
for (int i = 0; i < mCount; i++) {
float percent = (float) mValues[i] / 6f; // 计算数据值占比
float x = (float) (getMeasuredWidth() / 2f + mRadius * Math.cos(mAngle * i) * percent);
float y = (float) (getMeasuredHeight() / 2f + mRadius * Math.sin(mAngle * i) * percent);
if (i == 0) {
path.moveTo(x, getMeasuredHeight() / 2f);
} else {
path.lineTo(x, y);
}
// 绘制数据点
canvas.drawCircle(x, y, 5, mValuePaint);
}
path.close(); // 闭合路径
mValuePaint.setStyle(Paint.Style.FILL);
mValuePaint.setAlpha(127);
canvas.drawPath(path, mValuePaint);
}
}
```
在这个实现中,我们首先在 onMeasure 方法中计算出雷达图的半径和每个维度之间的角度。然后,在 onDraw 方法中先绘制雷达图,再绘制数据区域。在绘制雷达图时,我们通过计算每个多边形的边长和半径,以及每个维度的角度,来绘制多个同心多边形。然后,我们绘制多边形之间的连线和维度名称。在绘制数据区域时,我们通过计算每个数据值占比来绘制数据点,并使用 Path 来绘制闭合的数据区域。最后,我们再将数据区域填充上颜色。
这样,一个简单的雷达图就完成了。当然,这只是一个基础的实现,你可以根据自己的需求来进行更多的定制。
阅读全文