先看看效果图,别让文字把你们吓跑了.
下面开始讲解一下这个效果图的实现过程!!!!
首先,讲解一下思路:
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;
}
}
说明:本文章并非转载,如果有转载请注明转载说明,喜欢可以关注一下,后面会不定时分享技术.