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;
}
}
說明:本文章並非轉載,如果有轉載請註明轉載說明,喜歡可以關注一下,後面會不定時分享技術.