这是在学习android的Canvas绘图技巧时做的一个实例。主要用的核心方法就是canvas.save,canvas.rotate,
canvas.translate以及canvas.restore。通过这个小例子的练习,可以更好的掌握这些方法的使用。
先贴一张最终的效果图吧,如下:
来一步一步的来写代码吧。新建一个项目,然后新建类MyView继承自View,下面我们来首先将外盘画出来。即最外面的那个
圆。代码如下:
1 package com.example.testcanvas; 2 3 import android.content.Context; 4 import android.graphics.Canvas; 5 import android.graphics.Paint; 6 import android.util.AttributeSet; 7 import android.view.DragEvent; 8 import android.view.View; 9 /** 10 * 画一个仪表盘出来,哈哈 11 * @author fuly1314 12 * 13 */ 14 public class MyView extends View{ 15 16 private int width;//view宽度 17 private int height;//view的高度 18 private int radius;//外层圆的半径 22 public MyView(Context context, AttributeSet attrs, int defStyleAttr) { 23 super(context, attrs, defStyleAttr); 24 } 25 26 public MyView(Context context, AttributeSet attrs) { 27 super(context, attrs); 28 } 29 30 public MyView(Context context) { 31 super(context); 32 } 33 34 35 protected void onDraw(Canvas canvas) { 36 37 width = getWidth(); 38 height = getHeight(); 39 radius = width/2;//外部圆盘的半径 40 41 Paint paintCircle = new Paint(); 42 paintCircle.setStyle(Paint.Style.STROKE); 43 paintCircle.setStrokeWidth(5); 44 paintCircle.setAntiAlias(true); 45 //画出外层的圆盘 46 canvas.drawCircle(width/2, height/2, radius, paintCircle); 118 } 119 120 }
代码很简单,相信这一步大家都不陌生。我们将圆形置在了屏幕的中心位置,并让半径为宽度的一半。其他的就不多说了。
接下来我们来画刻度值。
首先刻度值有两种,一种是大的刻度值,这类刻度值都是可以被6整除的,另外一类则不可以。这个在画的时候很容易解决。
不容易解决的是要按照一定的弧度来把刻度值给画上。如果在这个时候,不利用canvcas.rotate方法的话,简直是要愁死人了。
还好有这么一个方法可以很方便的解决这个问题。我们先看代码,然后再来详细解释。增加的代码如下:
package com.example.testcanvas; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.util.AttributeSet; import android.view.DragEvent; import android.view.View; /** * 画一个仪表盘出来,哈哈 * @author fuly1314 * */ public class MyView extends View{ private int width;//view宽度 private int height;//view的高度 private int radius;//外层圆的半径 public MyView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); } public MyView(Context context) { super(context); } protected void onDraw(Canvas canvas) { width = getWidth(); height = getHeight(); radius = width/2;//外部圆盘的半径 Paint paintCircle = new Paint(); paintCircle.setStyle(Paint.Style.STROKE); paintCircle.setStrokeWidth(5); paintCircle.setAntiAlias(true); //画出外层的圆盘 canvas.drawCircle(width/2, height/2, radius, paintCircle); /** * 下面的代码要画出刻度值 */ for(int i =0;i<24;i++) { Paint paintDegree = new Paint(); if(i%6 == 0)//画的是大的刻度值 { paintDegree.setStrokeWidth(5); paintDegree.setTextSize(30); canvas.drawLine(width/2, height/2-radius, width/2, height/2-radius+60, paintDegree); canvas.drawText(String.valueOf(i), width/2-paintDegree.measureText(String.valueOf(i))/2, height/2-radius+90, paintDegree); }else//画的是小刻度 { paintDegree.setStrokeWidth(3); paintDegree.setTextSize(25); canvas.drawLine(width/2, height/2-radius, width/2, height/2-radius+30, paintDegree); canvas.drawText(String.valueOf(i), width/2-paintDegree.measureText(String.valueOf(i))/2, height/2-radius+60, paintDegree); } //将坐标系绕点(width/2,height/2)旋转15度 canvas.rotate(360/24, width/2, height/2); } } }
红色部分就是增加的画刻度线的代码。代码最核心的地方就是上面被黄色背景标注的地方。我们知道在画刻度的时候,刻度0是最好画的,如果
每一个刻度都能像画0刻度这么容易的话,岂不是太过瘾了!Android可谓在此时深深的了解你的愿望啊,于是Canvas.rotate方法就过来拯救你了!
你把一个圆分成了24份,每一份是15度,那么从画0刻度开始每次画一个刻度就把坐标系旋转15度,再来画下一个刻度,是不是就一直跟画0刻度的
方法是一样的了呢?显然是的!之所以应用rotate方法让坐标系围绕圆心旋转15度,可不就是为了这个嘛!因此你会发现在循环画刻度时,都是按照
画刻度0的画法来画的!!怎么样,神奇吧!至此,所有的刻度我们算是画好了。
最后我们来画两根指针。
再次修改代码如下:
1 package com.example.testcanvas; 2 3 import android.content.Context; 4 import android.graphics.Canvas; 5 import android.graphics.Paint; 6 import android.util.AttributeSet; 7 import android.view.DragEvent; 8 import android.view.View; 9 /** 10 * 画一个仪表盘出来,哈哈 11 * @author fuly1314 12 * 13 */ 14 public class MyView extends View{ 15 16 private int width;//view宽度 17 private int height;//view的高度 18 private int radius;//外层圆的半径 19 private int hcount = 0; 20 private int mcount = 0; 21 22 public MyView(Context context, AttributeSet attrs, int defStyleAttr) { 23 super(context, attrs, defStyleAttr); 24 } 25 26 public MyView(Context context, AttributeSet attrs) { 27 super(context, attrs); 28 } 29 30 public MyView(Context context) { 31 super(context); 32 } 33 34 35 protected void onDraw(Canvas canvas) { 36 37 width = getWidth(); 38 height = getHeight(); 39 radius = width/2;//外部圆盘的半径 40 41 Paint paintCircle = new Paint(); 42 paintCircle.setStyle(Paint.Style.STROKE); 43 paintCircle.setStrokeWidth(5); 44 paintCircle.setAntiAlias(true); 45 //画出外层的圆盘 46 canvas.drawCircle(width/2, height/2, radius, paintCircle); 47 48 /** 49 * 下面的代码要画出刻度值 50 */ 51 52 for(int i =0;i<24;i++) 53 { 54 55 Paint paintDegree = new Paint(); 56 57 if(i%6 == 0)//画的是大的刻度值 58 { 59 60 paintDegree.setStrokeWidth(5); 61 paintDegree.setTextSize(30); 62 63 canvas.drawLine(width/2, height/2-radius, width/2, 64 height/2-radius+60, paintDegree); 65 canvas.drawText(String.valueOf(i), width/2-paintDegree.measureText(String.valueOf(i))/2, 66 height/2-radius+90, paintDegree); 67 }else//画的是小刻度 68 { 69 paintDegree.setStrokeWidth(3); 70 paintDegree.setTextSize(25); 71 72 canvas.drawLine(width/2, height/2-radius, width/2, 73 height/2-radius+30, paintDegree); 74 canvas.drawText(String.valueOf(i), width/2-paintDegree.measureText(String.valueOf(i))/2, 75 height/2-radius+60, paintDegree); 76 77 } 78 79 //将坐标系绕点(width/2,height/2)旋转15度 80 canvas.rotate(360/24, width/2, height/2); 81 82 } 83 84 85 canvas.save();//先保存下,因为下面要用到坐标的平移 86 87 //将坐标系的平移至原点为(wdith/2,height/2)的地方 88 canvas.translate(width/2, height/2); 89 90 int hourRadius = radius*2/4; 91 int minuteRaidus = radius*3/4; 92 int hx = (int) (hourRadius*Math.cos(hcount)); 93 int hy = (int) (hourRadius*Math.sin(hcount)); 94 int mx = (int) (minuteRaidus*Math.cos(mcount)); 95 int my = (int) (minuteRaidus*Math.sin(mcount)); 96 97 Paint paintHour = new Paint(); 98 paintHour.setStrokeWidth(7); 99 100 canvas.drawLine(0, 0,hx , hy, paintHour); 101 102 Paint paintMinute = new Paint(); 103 paintMinute.setStrokeWidth(3); 104 105 canvas.drawLine(0, 0, mx, my, paintMinute); 106 107 canvas.restore(); 108 109 110 mcount++; 111 112 if(mcount%10 == 0){ 113 hcount++; 114 } 115 116 postInvalidateDelayed(500); 117 118 } 119 120 }
核心代码就是黄色背景标注的代码!因为在画指针的时候,我们发现还是不爽,还有从屏幕左上角来计算坐标。所以就使用canvas.translate方法
直接将坐标系给平移了。直接平移到圆心,因为我们的出发点就在圆形。接下来是不是画那些指针就十分容易了!在这里,我们使用了两个变量来控制
指针的转动分别是hcount和mcount。在转动的过程中怎么确定指针的坐标呢?画个图来说明吧。如下:
代码中的求指针的坐标的时候就是按照这个思路来求的。相信上面的代码应该没有什么问题了吧。当然了,代码写的乱,有很多可以优化的时候。
这里只是为了练习下canvas的方法,不去深究了。最后别忘记调用postInvalidateDelayed方法来进行刷新。
至此,这个能动的仪表盘就做好了。下面把它放进布局中吧。修改activity_main.xml代码如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <com.example.testcanvas.MyView android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout>
直接运行程序吧!效果跟上面的贴图一样。