导语
这种绘制属于自定义View的基本练习,网络上也有很多案例;可以根据需要将其中的代码进行修改,集成到项目中去:
效果:
很多游戏比赛中会有这种图,用于展示并比较选手的实力,算是比较直观的一种展示方式。
相关代码
自定义View的代码:
public class Polygon extends View {
private int mHeight;
private int mWidth;
//边框的画笔
private Paint mPaint;
//文字的画笔
private Paint textPaint;
//实力区域的画笔
private Paint realPaint;
//正n边型
private int count = 6;
//角度
private float angle = (float) (Math.PI * 2 / count);
//圆的半径
private float r = 50;
//等级分级的个数
private int levelCount = 5;
//一些标注
private String[] explains = {"反应", "英雄池", "操作", "意识", "大局", "团队"};
//文字大小
private int textSize = 30;
//文字与图形的距离
private int margin = 4;
//实力数据
private int[] realData;
//边框颜色
private int strokeColor = 0xFF000000;
//实力区域颜色
private int strengthColor = 0x800000ff;
//文字颜色
private int textColor = 0xFFFF0000;
//线的粗细
private int strokeWidth = 2;
//坐标
private float x;
private float y;
public Polygon(Context context) {
super(context);
}
public Polygon(Context context, AttributeSet attrs) {
super(context, attrs);
}
public Polygon(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
boolean isDraw = initData();
if (isDraw) {
canvas.translate(mWidth / 2, mHeight / 2);
initPaint();
drawPolygon(canvas);
drawLines(canvas);
drawText(canvas);
drawReal(canvas);
}
}
/** * 数据做一些校验 */
private boolean initData() {
boolean flag = true;
if (realData == null || realData.length == 0) {
flag = false;
return flag;
}
if (realData.length != count) {
throw new IllegalArgumentException("realData的大小必须为" + count + "个");
}
return flag;
}
/** * 绘制多边形 * * @param canvas */
private void drawPolygon(Canvas canvas) {
Path path = new Path();
for (int j = 1; j <= levelCount; j++) {
float r = this.r * j;
path.reset();
for (int i = 1; i <= count; i++) {
x = (float) (Math.cos(i * angle) * r);
y = (float) (Math.sin(i * angle) * r);
if (i == 1) {
path.moveTo(x, y);
} else {
path.lineTo(x, y);
}
}
path.close();
canvas.drawPath(path, mPaint);
}
}
/** * 绘制线条 * * @param canvas */
private void drawLines(Canvas canvas) {
Path path = new Path();
float r = this.r * levelCount;
for (int i = 1; i <= count; i++) {
path.reset();
//移动到中心
path.moveTo(0, 0);
x = (float) (Math.cos(i * angle) * r);
y = (float) (Math.sin(i * angle) * r);
path.lineTo(x, y);
canvas.drawPath(path, mPaint);
}
}
/** * 绘制文本;如果直接用x,y来绘制,丑陋不堪,这里做了些调整 * * @param canvas */
private void drawText(Canvas canvas) {
float r = this.r * levelCount;
for (int i = 0; i < count; i++) {
x = (float) (Math.cos(i * angle) * r);
y = (float) (Math.sin(i * angle) * r);
//文本
String text = explains[(i ) % explains.length];
//文本长度
float textLength = textPaint.measureText(text);
//说明:点在x轴上话,y理论上为0,但是现实很残酷,只是接近0,所以给了个粗糙的判断
if (y < 30 && y > -30 && x > 0) {
//x轴的正方向
x = x + margin;
y = y + textSize / 3;
canvas.drawText(text, x, y, textPaint);
} else if (y < 30 && y > -30 && x < 0) {
//x轴的负方向
x = x - textLength - margin;
y = y + textSize / 3;
canvas.drawText(text, x, y, textPaint);
} else if (x > 0 && y > 0) {
//第一象限
y = y + textSize + margin;
x = x - textSize / 2;
canvas.drawText(text, x, y, textPaint);
} else if (x > 0 && y < 0) {
//第二象限
x = x - textSize / 2;
y = y - margin;
canvas.drawText(text, x, y, textPaint);
} else if (x < 0 && y < 0) {
//第三象限
y = y - margin;
x = x - textLength / 2;
canvas.drawText(text, x, y, textPaint);
} else if (y > 0 && x < 0) {
//第四象限
y = y + textSize + margin;
x = x - textLength / 2;
canvas.drawText(text, x, y, textPaint);
}
/*打印坐标*/
// String print = String.format("x-->%f,y-->%f", x, y);
// Log.e("dd", print);
/*打印坐标*/
}
}
/** * 绘制实力状况 * * @param canvas */
private void drawReal(Canvas canvas) {
Path path = new Path();
for (int i = 0; i < realData.length; i++) {
int realLevel = realData[i];
if (realData == null || realData.length == 0) {
return;
}
if (realData.length != count) {
throw new IllegalArgumentException("realData的大小必须为" + count + "个");
}
if (realLevel < 0 || realLevel > levelCount) {
throw new IllegalArgumentException(String.format("水平数据必须大于等于0且小于等于%d", levelCount));
}
float r = this.r * realLevel;
x = (float) (Math.cos(i * angle) * r);
y = (float) (Math.sin(i * angle) * r);
if (i == 0) {
path.moveTo(x, y);
} else {
path.lineTo(x, y);
}
}
path.close();
canvas.drawPath(path, realPaint);
}
/** * 初始化画笔 */
private void initPaint() {
if (mPaint != null)
return;
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(strokeColor);
mPaint.setStrokeWidth(strokeWidth);
textPaint = new Paint();
textPaint.setTextSize(textSize);
textPaint.setColor(textColor);
textPaint.setAntiAlias(true);
realPaint = new Paint();
realPaint.setColor(strengthColor);
realPaint.setAntiAlias(true);
realPaint.setStyle(Paint.Style.FILL_AND_STROKE);
}
/** * 设置数据 * * @param realData */
public void setRealData(int[] realData) {
this.realData = realData;
invalidate();
}
}
Activity中的代码:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initPolygon();
}
private void initPolygon() {
setContentView(R.layout.polygon);
Polygon poly = (Polygon) findViewById(R.id.poly);
poly.setRealData(new int[]{1, 2, 3, 4, 5, 4});
}
}
xml中的代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
<com.example.niugulu.viewstudy.view.Polygon android:id="@+id/poly" android:layout_width="match_parent" android:layout_height="match_parent" />
</LinearLayout>
如果项目急用,可以把上面的代码复制到demo中,看下效果,根据需求做一些更改;如果想要了解制作流程,且慢慢看:
步骤分解
先来一种动态图压压惊:
分析一下步骤:
- 画一个正六边形
- 画N个正六边形
- 从中心到顶点用直线连接
- 在顶点附近写上文字
- 将实际的分数绘制到图形中,并展示出相应的图案
重点代码的解析
最难得部分是会知正六边形,知道如何会知正六边形,其余的部分就不在话下了。
如果你在网上搜索割圆术,会有以下印象:
割圆术的描述(来自 *)
刘徽割圆术是建立在圆面积论的基础之上的。他首先论证,将圆分割成多边形,分割来越细,多边形的边数越多,多边形的面积就和圆面积没有差别了。他说,将6边形一边的长度乘以圆半径,再乘3,得12边形的面积。将12边形的一边长乘半径,再乘6,得24边形面积。越割越细,多边形和圆面积的差越小。如此割了再割,最后终于和圆合为一体,毫无差别了。
用点逆向思维:如果知道一个圆,可以在圆的内部画出相应大小的正多边形。
下面这段代码片,就是根据一个圆,来绘制相应的多边形
//正n边型
private int count = 6;
//角度(360/count)
private float angle = (float) (Math.PI * 2 / count);
//圆的半径
private float r = 50;
//绘制点的坐标
private float x;
private float y;
//去绘制
for (int i = 1; i <= count; i++) {
//根据高中数学的知识,去求相应的坐标
x = (float) (Math.cos(i * angle) * r);
y = (float) (Math.sin(i * angle) * r);
if (i == 1) {
//当i为1时,该点为起点,不必与之前的点建立链接;所以使用moveTo,而不用lineTo
path.moveTo(x, y);
} else {
path.lineTo(x, y);
}
}
path.close();
canvas.drawPath(path, mPaint);
知道正多边形如何绘制,其余的代码也就没有什么难度了,只是一些细节的处理,花点时间,就可以搞定。
结语
自定义View主要是分解步骤,能把图形绘制的流程分割成若干步,任务基本上就完成了一半。
转载请标明出处:http://blog.csdn.net/qq_26411333/article/details/52330925