Android自定义控件 -Canvas绘制折线图(实现动态报表效果)
有时候我们在项目中会遇到使用折线图等图形,Android的开源项目中为我们提供了很多插件,但是很多时候我们需要根据具体项目自定义这些图表,这一篇文章我们一起来看看如何在Android中使用Canvas绘制折线图。先看看绘制的效果:
代码:
public class MyView extends View {
//坐标轴原点的位置
private int xPoint=60;
private int yPoint=260;
//刻度长度
private int xScale=8; //8个单位构成一个刻度
private int yScale=40;
//x与y坐标轴的长度
private int xLength=380;
private int yLength=240;
private int MaxDataSize=xLength/xScale; //横坐标 最多可绘制的点
private List<Integer> data=new ArrayList<Integer>(); //存放 纵坐标 所描绘的点
private String[] yLabel=new String[yLength/yScale]; //Y轴的刻度上显示字的集合
private Handler mh=new Handler(){
public void handleMessage(android.os.Message msg) {
if(msg.what==0){ //判断接受消息类型
MyView.this.invalidate(); //刷新View
}
};
};
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
for (int i = 0; i <yLabel.length; i++) {
yLabel[i]=(i+1)+"M/s";
}
new Thread(new Runnable() {
@Override
public void run() {
while(true){ //在线程中不断往集合中增加数据
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(data.size()>MaxDataSize){ //判断集合的长度是否大于最大绘制长度
data.remove(0); //删除头数据
}
data.add(new Random().nextInt(5)+1); //生成1-6的随机数
mh.sendEmptyMessage(0); //发送空消息通知刷新
}
}
}).start();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint=new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setAntiAlias(true);
paint.setColor(Color.RED);
//绘制Y轴
canvas.drawLine(xPoint, yPoint-yLength, xPoint, yPoint, paint);
//绘制Y轴左右两边的箭头
canvas.drawLine(xPoint, yPoint-yLength, xPoint-3,yPoint-yLength+6, paint);
canvas.drawLine(xPoint, yPoint-yLength, xPoint+3,yPoint-yLength+6, paint);
//Y轴上的刻度与文字
for (int i = 0; i * yScale< yLength; i++) {
canvas.drawLine(xPoint, yPoint-i*yScale, xPoint+5, yPoint-i*yScale, paint); //刻度
canvas.drawText(yLabel[i], xPoint-50, yPoint-i*yScale, paint);//文字
}
//X轴
canvas.drawLine(xPoint, yPoint, xPoint+xLength, yPoint, paint);
//如果集合中有数据
if(data.size()>1){
for (int i = 1; i < data.size(); i++) { //依次取出数据进行绘制
canvas.drawLine(xPoint+(i-1)*xScale, yPoint-data.get(i-1)*yScale, xPoint+i*xScale, yPoint-data.get(i)*yScale, paint);
}
}
}
}
上面绘制的折线使用的canvas.drawLine方法,还可以用canvas.drawPath方法实现.
//实现的另一种方式
if(data.size()>1){
Path path=new Path();
path.moveTo(xPoint, yPoint-data.get(0)*yScale);//起点
for (int i = 1; i < data.size(); i++) {
path.lineTo(xPoint+i*xScale, yPoint-data.get(i)*yScale);
}
canvas.drawPath(path, paint);
}
如果需求是这样的:
public class MyView extends View {
//坐标轴原点的位置
private int xPoint=60;
private int yPoint=260;
//刻度长度
private int xScale=8; //8个单位构成一个刻度
private int yScale=40;
//x与y坐标轴的长度
private int xLength=380;
private int yLength=240;
private int MaxDataSize=xLength/xScale; //横坐标 最多可绘制的点
private List<Integer> data=new ArrayList<Integer>(); //存放 纵坐标 所描绘的点
private String[] yLabel=new String[yLength/yScale]; //Y轴的刻度上显示字的集合
private Handler mh=new Handler(){
public void handleMessage(android.os.Message msg) {
if(msg.what==0){ //判断接受消息类型
MyView.this.invalidate(); //刷新View
}
};
};
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
for (int i = 0; i <yLabel.length; i++) {
yLabel[i]=(i+1)+"M/s";
}
new Thread(new Runnable() {
@Override
public void run() {
while(true){ //在线程中不断往集合中增加数据
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(data.size()>MaxDataSize){ //判断集合的长度是否大于最大绘制长度
data.remove(0); //删除头数据
}
data.add(new Random().nextInt(5)+1); //生成1-6的随机数
mh.sendEmptyMessage(0); //发送空消息通知刷新
}
}
}).start();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint=new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setAntiAlias(true);
paint.setColor(Color.RED);
//绘制Y轴
canvas.drawLine(xPoint, yPoint-yLength, xPoint, yPoint, paint);
//绘制Y轴左右两边的箭头
canvas.drawLine(xPoint, yPoint-yLength, xPoint-3,yPoint-yLength+6, paint);
canvas.drawLine(xPoint, yPoint-yLength, xPoint+3,yPoint-yLength+6, paint);
//Y轴上的刻度与文字
for (int i = 0; i * yScale< yLength; i++) {
canvas.drawLine(xPoint, yPoint-i*yScale, xPoint+5, yPoint-i*yScale, paint); //刻度
canvas.drawText(yLabel[i], xPoint-50, yPoint-i*yScale, paint);//文字
}
//X轴
canvas.drawLine(xPoint, yPoint, xPoint+xLength, yPoint, paint);
//实现填充
paint.setStyle(Paint.Style.FILL);
if(data.size()>1){
Path path=new Path();
path.moveTo(xPoint, yPoint);
for (int i = 0; i < data.size(); i++) {
path.lineTo(xPoint+i*xScale, yPoint-data.get(i)*yScale);
}
path.lineTo(xPoint+(data.size()-1)*xScale, yPoint);
canvas.drawPath(path, paint);
}
}
}
如果还有这种需求:
public class MyView extends View {
//坐标轴原点的位置
private int xPoint=60;
private int yPoint=260;
//刻度长度
private int xScale=8; //8个单位构成一个刻度
private int yScale=40;
//x与y坐标轴的长度
private int xLength=380;
private int yLength=240;
private int MaxDataSize=xLength/xScale; //横坐标 最多可绘制的点
private List<Integer> data=new ArrayList<Integer>(); //存放 纵坐标 所描绘的点
private String[] yLabel=new String[yLength/yScale]; //Y轴的刻度上显示字的集合
private Handler mh=new Handler(){
public void handleMessage(android.os.Message msg) {
if(msg.what==0){ //判断接受消息类型
MyView.this.invalidate(); //刷新View
}
};
};
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
for (int i = 0; i <yLabel.length; i++) {
yLabel[i]=(i+1)+"M/s";
}
new Thread(new Runnable() {
@Override
public void run() {
while(true){ //在线程中不断往集合中增加数据
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(data.size()>MaxDataSize){ //判断集合的长度是否大于最大绘制长度
data.remove(0); //删除头数据
}
data.add(new Random().nextInt(5)+1); //生成1-6的随机数
mh.sendEmptyMessage(0); //发送空消息通知刷新
}
}
}).start();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint=new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setAntiAlias(true);
paint.setColor(Color.RED);
//绘制Y轴
canvas.drawLine(xPoint, yPoint-yLength, xPoint, yPoint, paint);
//绘制Y轴左右两边的箭头
canvas.drawLine(xPoint, yPoint-yLength, xPoint-3,yPoint-yLength+6, paint);
canvas.drawLine(xPoint, yPoint-yLength, xPoint+3,yPoint-yLength+6, paint);
//Y轴上的刻度与文字
for (int i = 0; i * yScale< yLength; i++) {
canvas.drawLine(xPoint, yPoint-i*yScale, xPoint+5, yPoint-i*yScale, paint); //刻度
canvas.drawText(yLabel[i], xPoint-50, yPoint-i*yScale, paint);//文字
}
//X轴
canvas.drawLine(xPoint, yPoint, xPoint+xLength, yPoint, paint);
paint.setStrokeWidth(5);
Paint paint2=new Paint();
paint2.setColor(Color.BLACK);
paint2.setStyle(Paint.Style.FILL);
if(data.size()>1){
Path path=new Path();
Path path2=new Path();
path.moveTo(xPoint, yPoint-data.get(0)*yScale);
path2.moveTo(xPoint, yPoint);
for (int i = 0; i < data.size(); i++) {
path.lineTo(xPoint+i*xScale, yPoint-data.get(i)*yScale);
path2.lineTo(xPoint+i*xScale, yPoint-data.get(i)*yScale);
}
path2.lineTo(xPoint+(data.size()-1)*xScale, yPoint);
canvas.drawPath(path, paint);
canvas.drawPath(path2, paint2);
}
}
}
这种动态的效果在模拟器上显示会出现问题,真机正常.
qq3061280@163.com