有時候我們在項目中會遇到使用折線圖等圖形,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);
}
}
}
這種動態的效果在模擬器上顯示會出現問題,真機正常.