Java代码
public class EvaluationView extends View {
private int mNormalColor;
private int mHighlightColor;
private int mLineCount;
private int mGeometryCount;
private float mNormalStrokeWidth;
private float mHighlightStrokeWidth;
private boolean mhighlightFill;
private boolean needUpWords;
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) {
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++) {
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) {
width = AppUtils.dp2px(500 , getResources().getDisplayMetrics());
}
if (heightMode != MeasureSpec.EXACTLY) {
height = AppUtils.dp2px(200 , getResources().getDisplayMetrics());
}
setMeasuredDimension(width, height);
mCenterX = width / 2 ;
mCenterY = height / 2 ;
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" />
效果