這是在學習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>
直接運行程序吧!效果跟上面的貼圖一樣。