1. 程式人生 > >Android實現蜘蛛網圖繪製

Android實現蜘蛛網圖繪製

先看看效果圖,別讓文字把你們嚇跑了.

下面開始講解一下這個效果圖的實現過程!!!!

首先,講解一下思路:

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;
    }


}


說明:本文章並非轉載,如果有轉載請註明轉載說明,喜歡可以關注一下,後面會不定時分享技術.