Android实现蜘蛛网图绘制

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/QQ243223991/article/details/56844133

先看看效果图,别让文字把你们吓跑了.

下面开始讲解一下这个效果图的实现过程!!!!

首先,讲解一下思路:

1.绘制网格(4个六边形)

2.绘制6条轴

3.绘制顶点

4.根据顶点坐标绘制path

5.利用随机函数随机生成一组数值并展示

思路就是这些,在知道怎么去完成这件事件后,我们还需要知道完成这件事件需要的准备工作,接下来就说说一些准备工作.

准备工作:

1.对Android坐标轴的理解

2.对Canvas操作有一定的了解

3.对Path的绘制有一定了解

4.初中数学知识

我们依次道来~

Android系统中的坐标粗略概念

在数学概念中,我们的坐标轴是以水平向右延伸的线条为x轴,以竖直向上延伸的线条为y轴,如图所示:

在Android中,坐标原点位于屏幕的左上角,水平向右为x轴,竖直向下为y轴,如图中图1所示,因此我们需要将Android的坐标系转换成我们熟悉的数学中的坐标系的样子来进行绘图操作,这样理解起来就不会吃力.

Canvas的基本功能

Canvas翻译过来是"帆布,画布"之意,好比绘画中的画板上的纸.因此我们可以在上面自有发挥我们想要画的内容,在生活中,除了画纸,还需要画笔来进行绘画,而画笔有不同颜色,不同粗细的画笔,Android绘图也一样,需要画笔来绘制,即Paint,同理,Paint也可以根据自己需要进行颜色设置,粗细设置等等.有了画笔之后我们就可以尽情的绘制了,在小学,我们在美术课堂上是从基本的线条,基本图形开始学习画画,这里也一样,因此canvas可以绘制

点:

drawPoint(...);

线:

drawLine(...);

矩形:

drawRect(...);

圆形

drawCircle(...);

三角形:

drawArc(...);

等等.还有一个路径绘制drawPath(...),我们这里只做一些基本的方法讲解,更详细的Canvas用法可以自行百度.

Path

翻译过来即为"路径",我们知道,两点可以确定一条线,Path也不例外,因此就有了moveTo(int,int),lineTo(int,int)等等方法

在大概知道这些准备工作后,我们还需要用到初中所学的正余弦函数来进行坐标计算.在了解这些准备工作和思路之后,我们来正式进行代码编写吧!

第一步:

我们需要对对应的"画板"和"画笔"等进行声明并初始化

// 画笔对象
    private Paint mPaint;

    // 绘制6条坐标轴
    private Path mPath;

    // view宽度
    private int mWidth;

    // view高度
    private int mHeight;
    /**
     * 范围在(0,400],默认数值
     */
    private int[] values = {100, 150, 200, 150, 300, 400};
/**
     * 初始化画笔相关
     * @param context 上下文
     * @param attrs 属性
     */
    private void init(Context context, AttributeSet attrs) {
        // 实例化画笔
        mPaint = new Paint();

        // 设置画笔样式为描边模式
        mPaint.setStyle(Paint.Style.STROKE);

        // 开启抗锯齿
        mPaint.setAntiAlias(true);

        // 实例化坐标轴path
        mPath = new Path();

        // 移动至坐标原点
        mPath.moveTo(0, 0);

    }


第二步:将Android系统中的坐标系转换成我们熟悉的数学坐标系,如下图,

// 设置画笔颜色为蓝色
        mPaint.setColor(Color.BLUE);

        // 设置画笔宽度为1
        mPaint.setStrokeWidth(1);

        // 移动画布到屏幕中心
        canvas.translate(mWidth / 2, mHeight / 2);

        // 第1次保存画布在屏幕中心画布状态,restoreCount为0
        canvas.save();

        // 坐标以X轴翻转
        canvas.scale(1, -1);

        // 第2次,保存坐标以x轴翻转的画布状态,restoreCount为1
        canvas.save();


 

第三步:绘制网格:

// 绘制网络
    private void drawNet(Canvas canvas) {
        // 第2-5保存旋转后的线条状态
        canvas.save();
        for (int i = 0; i < 5; i++) {
            drawOneNet(canvas, i * 100);
            if (i == 4) {
                drawLines(canvas, i * 100);
            }
        }
    }

/**
     * 绘制网格
     * @param canvas
     * @param width
     */
    private void drawOneNet(Canvas canvas, int width) {
        int height = (int) (width * Math.cos(Math.toRadians(30)));
        for (int i = 0; i < 6; i++) {
            canvas.restore();
            canvas.drawLine(-width / 2, height, width / 2, height, mPaint);
            canvas.rotate(60, 0, 0);
            canvas.save();
        }
    }

/**
     * 绘制线条
     *
     * @param canvas
     */
    private void drawLines(Canvas canvas, int width) {
        for (int i = 0; i < 6; i++) {
            canvas.restore();
            if (i == 0) {
                canvas.rotate(30, 0, 0);
            } else {
                canvas.rotate(60, 0, 0);
            }
            mPath.lineTo(0, width);
            canvas.drawPath(mPath, mPaint);
            canvas.save();
        }
    }

到这里我们可以得到如果效果:

好的,有点样子了,继续往下看.

第四步:绘制顶点

/**
     * 绘制素所有坐标顶点
     *
     * @param canvas
     */
    private void drawPoints(Canvas canvas, int[] values) {
        Point[] points = new Point[values.length];
        // 绘制顶点
        for (int i = 0; i < values.length; i++) {
            Point point = getRotatePoint(i, values[i]);
            points[i] = point;
            drawPoint(point, canvas);
        }

    }


第五步:连接各个顶点

/**
     * 连接各个顶点
     *
     * @param canvas 画笔工具
     * @param points 顶点
     */
    private void point2Line(Canvas canvas, Point[] points) {
        canvas.restoreToCount(1);
        Path path = new Path();
        Paint paint = new Paint();
        paint.setColor(0x7f1aaf03);// 50%的透明度的绿色
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.FILL);
        paint.setStrokeWidth(4);

        path.moveTo(points[0].getX(), points[0].getY());
        for (int i = 1; i < points.length; i++) {
            path.lineTo(points[i].getX(), points[i].getY());
        }
        // 闭合线路
        path.close();
        canvas.drawPath(path, paint);
    }

这样我们就大功告成了!


 

下面贴上完整代码

MainActivity

public class MainActivity extends AppCompatActivity {

    private Button mBtnStart;
    private PathView mPathView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mPathView = (PathView) findViewById(R.id.pathView);
        mBtnStart = (Button) findViewById(R.id.btn_start);
        mBtnStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int[] values = rand();
                mPathView.setValues(values);
            }
        });
    }

    private int[] rand() {
        int[] values = new int[6];
        for (int i = 0; i < 6; i++) {
            int value = new Random().nextInt(400)%(301) + 100;
            values[i] = value;
        }
        return values;
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#ffffff"
    >

    <com.android.anqiansong.pathdemo.PathView
        android:layout_width="match_parent"
        android:layout_height="400dp"
        android:id="@+id/pathView" />
    <Button
        android:id="@+id/btn_start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="切换"
        />


</LinearLayout>

PathView

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;


/**
 * path学习
 */

public class PathView extends View {

    // 画笔对象
    private Paint mPaint;

    // 绘制6条坐标轴
    private Path mPath;

    // view宽度
    private int mWidth;

    // view高度
    private int mHeight;
    /**
     * 范围在(0,400],默认数值
     */
    private int[] values = {100, 150, 200, 150, 300, 400};

    public PathView(Context context) {
        super(context);
        init(context, null);
    }

    public PathView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public PathView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    public PathView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
    }

    /**
     * 初始化画笔相关
     * @param context 上下文
     * @param attrs 属性
     */
    private void init(Context context, AttributeSet attrs) {
        // 实例化画笔
        mPaint = new Paint();

        // 设置画笔样式为描边模式
        mPaint.setStyle(Paint.Style.STROKE);

        // 开启抗锯齿
        mPaint.setAntiAlias(true);

        // 实例化坐标轴path
        mPath = new Path();

        // 移动至坐标原点
        mPath.moveTo(0, 0);

    }


    /**
     * 先绘制网格
     * 其次,绘制5条直线,并设置值,旋转度数,计算旋转后顶点的坐标
     * 第三,根据顶点绘制path
     *
     * @param canvas
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // 设置画笔颜色为蓝色
        mPaint.setColor(Color.BLUE);

        // 设置画笔宽度为1
        mPaint.setStrokeWidth(1);

        // 移动画布到屏幕中心
        canvas.translate(mWidth / 2, mHeight / 2);

        // 第1次保存画布在屏幕中心画布状态,restoreCount为0
        canvas.save();

        // 坐标以X轴翻转
        canvas.scale(1, -1);

        // 第2次,保存坐标以x轴翻转的画布状态,restoreCount为1
        canvas.save();

        // 绘制网格
        drawNet(canvas);

        // 绘制所有坐标顶点
        drawPoints(canvas, values);

    }


    /**
     * 动态设置值
     *
     * @param values 待展示值,每个坐标不能超过400
     */
    public void setValues(int[] values) {
        this.values = values;
        invalidate();// 重新绘制
    }

    /**
     * 连接各个顶点
     *
     * @param canvas 画笔工具
     * @param points 顶点
     */
    private void point2Line(Canvas canvas, Point[] points) {
        canvas.restoreToCount(1);
        Path path = new Path();
        Paint paint = new Paint();
        paint.setColor(0x7f1aaf03);// 50%的透明度的绿色
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.FILL);
        paint.setStrokeWidth(4);

        path.moveTo(points[0].getX(), points[0].getY());
        for (int i = 1; i < points.length; i++) {
            path.lineTo(points[i].getX(), points[i].getY());
        }
        // 闭合线路
        path.close();
        canvas.drawPath(path, paint);
    }

    // 绘制网络
    private void drawNet(Canvas canvas) {
        // 第2-5保存旋转后的线条状态
        canvas.save();
        for (int i = 0; i < 5; i++) {
            drawOneNet(canvas, i * 100);
            if (i == 4) {
                drawLines(canvas, i * 100);
            }
        }
    }

    /**
     * 绘制素所有坐标顶点
     *
     * @param canvas
     */
    private void drawPoints(Canvas canvas, int[] values) {
        Point[] points = new Point[values.length];
        // 绘制顶点
        for (int i = 0; i < values.length; i++) {
            Point point = getRotatePoint(i, values[i]);
            points[i] = point;
            drawPoint(point, canvas);
        }

        // 连接各个顶点
        point2Line(canvas, points);
    }


    /**
     * 绘制网格
     * @param canvas
     * @param width
     */
    private void drawOneNet(Canvas canvas, int width) {
        int height = (int) (width * Math.cos(Math.toRadians(30)));
        for (int i = 0; i < 6; i++) {
            canvas.restore();
            canvas.drawLine(-width / 2, height, width / 2, height, mPaint);
            canvas.rotate(60, 0, 0);
            canvas.save();
        }
    }

    /**
     * 绘制线条
     *
     * @param canvas
     */
    private void drawLines(Canvas canvas, int width) {
        for (int i = 0; i < 6; i++) {
            canvas.restore();
            if (i == 0) {
                canvas.rotate(30, 0, 0);
            } else {
                canvas.rotate(60, 0, 0);
            }
            mPath.lineTo(0, width);
            canvas.drawPath(mPath, mPaint);
            canvas.save();
        }
    }

    /**
     * 绘制坐标顶点
     *
     * @param point 坐标顶点
     */
    private void drawPoint(Point point, Canvas canvas) {
        // 回滚到第二次保存状态
        canvas.restoreToCount(1);
        mPaint.setColor(Color.RED);
        mPaint.setStrokeWidth(10);
        canvas.drawPoint(point.getX(), point.getY(), mPaint);
    }

    /**
     * 根据值计算旋转后的坐标顶点
     *
     * @param index 第几条边,起始边为1点钟方向那条边
     * @param value 待计算值
     * @return
     */
    private Point getRotatePoint(int index, int value) {
        int degrees = index * 60 + 30;
        double radians = Math.toRadians(degrees);
        double sin = Math.sin(radians);
        double cos = Math.cos(radians);
        int x = (int) (sin * value);
        int y = (int) (cos * value);
        Log.d("tag", "x:" + x + ",y:" + y);
        Point point = new Point();
        point.setX(x);
        point.setY(y);
        return point;
    }


}


说明:本文章并非转载,如果有转载请注明转载说明,喜欢可以关注一下,后面会不定时分享技术.
 

猜你喜欢

转载自blog.csdn.net/QQ243223991/article/details/56844133