Android绘制选手实力图

时间:2023-01-08 18:21:26

导语

这种绘制属于自定义View的基本练习,网络上也有很多案例;可以根据需要将其中的代码进行修改,集成到项目中去:
效果:
Android绘制选手实力图
很多游戏比赛中会有这种图,用于展示并比较选手的实力,算是比较直观的一种展示方式。

相关代码

自定义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中,看下效果,根据需求做一些更改;如果想要了解制作流程,且慢慢看:

步骤分解

先来一种动态图压压惊:

Android绘制选手实力图

分析一下步骤:

  1. 画一个正六边形
  2. 画N个正六边形
  3. 从中心到顶点用直线连接
  4. 在顶点附近写上文字
  5. 将实际的分数绘制到图形中,并展示出相应的图案

重点代码的解析

最难得部分是会知正六边形,知道如何会知正六边形,其余的部分就不在话下了。

如果你在网上搜索割圆术,会有以下印象:

Android绘制选手实力图

割圆术的描述(来自 *

刘徽割圆术是建立在圆面积论的基础之上的。他首先论证,将圆分割成多边形,分割来越细,多边形的边数越多,多边形的面积就和圆面积没有差别了。他说,将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