我們平常的畫圖都是通過Paint畫筆在Canvas畫布上進行繪製的,我們在畫圖之前首先就是要把我們的畫筆設置好。就跟我們平時畫圖一樣,先要選好畫筆的粗細、顏色以及透明度,然後我們纔開始作畫,最後將Canvas畫布呈現給用戶。
我們來認識一些具體的方法:
畫圖之前我們是要準備好Pint(畫筆)的,那麼我們就來整一個畫筆對象。
先了解一下畫筆的一些樣式
Paint.Style.FILL:填充內部也就是我們平時所說的實心的
Paint.Style.FILL_AND_STROKE :填充內部和描邊 描邊其實就是我們事先畫好一個模型,然後再填充顏色空心圓到實心圓的變化
Paint.Style.STROKE :描邊也就是我們所說的空心的
Paint paint= new Paint(Paint.ANTI_ALIAS_FLAG);
Paint.ANTI_ALIAS_FLAG就是消除鋸齒使用以後邊界就會變的稍微有點模糊,鋸齒就看不到了
paint.setColor(Color.RED)設置畫筆的顏色,畫筆的顏色可以是系統的也可以自己設置
paint.setStrokeWidth(3);設置空心畫筆的寬度,畫筆的粗細可以通過此方法進行設置
有點也就有線,canvas裏面有一個drawLine方法可以通過canvas.drawLine來畫一條線。有線也有面的我們可以通過
canvas.drawLine(100,100,200,200,paint);裏面的前四個參數分別是float startX, float startY, float stopX, float stopY。因爲我們說話的其實是一條對角線,所以我們就以下面的矩形草圖的AC兩點來說明。startX則相當於A點的X座標,startY則是A點的Y座標,stopX則相當於C點的X座標,stopY則是C點的Y座標。
canvas.drawRect方法來畫一個區域
示例代碼爲
@Override
protected void onDraw(Canvas canvas) {
canvas.drawLine(100,100,200,200,paint);
canvas.drawRect(100,300,200,400,paint);
}
我們還要了解一下paint的一些其他樣式,以備用時之需。
paint.setStrokeJoin(Join join)設置結合處的樣子使用爲paint.setStrokeJoin(Paint.Join.ROUND),Round:結合處爲圓弧,Miter:結合處爲銳角, BEVEL:結合處爲直線。設置時要注意:Paint.Style.FILL爲默認的畫筆樣式,但是此種樣式效果會顯示不出來,所以建議使用另外Paint.Style.FILL_AND_STROKE 和Paint.Style.STROKE 兩種樣式其中的一種。
效果圖爲
示例代碼爲
@Override
protected void onDraw(Canvas canvas) {
paint.setStrokeWidth(20);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
canvas.drawRect(160, 80, 250, 180, paint);
paint.setStrokeJoin(Paint.Join.MITER);
canvas.drawRect(160, 240, 250, 340, paint);
paint.setStrokeJoin(Paint.Join.BEVEL);
canvas.drawRect(160, 400, 250, 500, paint);
}
setStrokeCap(Cap cap)設置線末端的
paint.setStrokeCap(Paint.Cap.ROUND);它也有三種樣式分別爲ROUND、BUTT、SQUARE畫筆樣式雖然三種都可以設置,但是此種樣式下顯示的沒有空心效果的,而且畫筆在顯示的區域內越粗效果越明顯。由於是設置線的末端因此要調用的是canvas.drawLine的方法。
效果圖爲
示例代碼爲
@Override
protected void onDraw(Canvas canvas) {
paint.setStrokeWidth(100);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeCap(Paint.Cap.ROUND);
canvas.drawLine(160, 80, 250, 80, paint);
paint.setStrokeCap(Paint.Cap.BUTT);
canvas.drawLine(160, 240, 250, 240, paint);
paint.setStrokeCap(Paint.Cap.SQUARE);
canvas.drawLine(160, 400, 250, 400, paint);
}
Path類可以預先在View上將N個點連成一條”路徑”,然後調用Canvas的drawPath(path,paint)即可沿着路徑繪製圖形
使用path也是要實例化一個對象然後調用其內部方法。
下面我們先來畫一個三角形
示例代碼爲
@Override
protected void onDraw(Canvas canvas) {
Path path=new Path();
path.moveTo(210,210);
path.lineTo(210,350);
path.lineTo(280,350);
canvas.drawPath(path,paint);
}
畫圖肯定是是起筆點的位置,path.moveTo就是先把一個起點移動到屏幕的某個位置,然後再以這一點開始移動到其他座標點連城線,圖形的形成就是先有點到線,再有先到面,點與點之間連成線,線與線之間連接起來就是一個面,上面的代碼就是把起點移動到下圖(下圖只是一個草圖,方便講解而已)的A點path.moveTo(210,210);public void moveTo(float x, float y)通過對比很容易發現,參數裏面的第一個210就是Ax ,第二個210就是Ay。path.lineTo(210,350)lineTo很明顯就是連線到的意思,就是在起點和lineTo的座標點之間連成一條線(AB),最後一個lineTo也是一樣,不過是與前面lineTo的點連成一條線(BC),最後就是閉合,也就是最後一個lineTo點連接起點,形成一條線也就是我們看到的(AC),我們看到的圖形就是這麼形成的。
關於四邊形的我們上面的canvas.drawRect(100,300,200,400,paint);
如果不知道如何設置float left, float top, float right, float bottom, 這幾個參數,可以參考下面的關於正方形ABCD四個頂點的草圖。left就是A和B的X座標值,top就是A和D的Y座標值,right就是C和D的X座標值,bottom就是B和C的Y座標值。記住這幾點就會很容易的運用drawRect方法畫出我們想要的矩形了。
已經實現過了,但是我們也可以通過path來完成
圖形還是那個圖形
示例代碼爲
@Override
protected void onDraw(Canvas canvas) {
Path path=new Path();
path.moveTo(100,300);
path.lineTo(100,400);
path.lineTo(200,400);
path.lineTo(200,300);
canvas.drawPath(path,paint);
}
作圖前要先想好畫什麼樣的圖形,如果是正方形,那麼就要考慮到正方形的四個頂點的位置,如何確定呢,關於lineTo上面畫三角形的時候已經介紹過,可以看一下。我們首先要確定四個頂點,起點的位置可以隨意錨點,但是剩下的三個點就要按照一定的方式去設置了。如下圖,加入我們以A點爲起點,那麼B點的X座標就要跟A點的一樣,那麼Y軸呢?如果我們想畫一個邊長爲100的正方形,我們就要在A點座標的Y值上面加上100設置爲B的Y點座標值,這樣B座標就確定了,C點的座標跟B點座標的Y值是一樣的,X座標值是什麼呢?我們可以看到C和B是在一條水平線上,C在B的右側那麼就要在B點的X座標值上面加上100設置爲C點座標的X值,確定了B和C兩點座標的值就可以確定D點的了,因爲D點座標的X值其實就是A的Y座標值,X值就是C的X座標值。這樣我們就可以完美的畫出上面的矩形了。
我們如果想要扇形怎麼辦?我們可以通過canvas的drawArc方法來實現。drawArc方法裏面含有四個參數分辨是:oval :指定圓弧的外輪廓矩形區域。startAngle: 圓弧起始角度,就是我們畫筆的起點位置,單位爲度。sweepAngle: 圓弧掃過的角度,也就是想要畫的扇形的弧度,順時針方向,單位爲度。useCenter: 如果爲True時,在繪製圓弧時將圓心包括在內,通常用來繪製扇形。我們先來實現一下
示例代碼爲
@Override
protected void onDraw(Canvas canvas) {
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.STROKE);
canvas.drawRect(50, 300, 150, 400, paint);
RectF rect = new RectF(50, 300, 150, 400);
canvas.drawArc(rect, -180, 180, true, paint);
paint.setStyle(Paint.Style.STROKE);
RectF rect1 = new RectF(200, 300, 300, 400);
canvas.drawArc(rect1, -180, 180, true, paint);
paint.setStyle(Paint.Style.STROKE);
RectF rect2 = new RectF(350, 300, 450, 400);
canvas.drawArc(rect2, -180, 180, false,paint);
}
之所以第一個畫出矩形是爲了讓大家知道,矩形就是扇形顯示的區域,我們設置的是一個內切圓,矩形的中心就是圓心。通過上面的代碼和效果圖我們可以看到useCenter: 如果爲True時,在繪製圓弧時將圓心包括在內,來繪製扇形,如果爲false的話就是一個弧線。
下面我們來實現一個大家都見過的一個自己畫的鐘表
示例代碼
package demo.liuyongxiang.com.demo.activities;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.os.Bundle;
import android.view.View;
/**
* Created by ytx on 2016/11/10.
*/
public class MyCanvas extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new CustomView1(this));
}
class CustomView1 extends View {
Paint paint;
public CustomView1(Context context) {
super(context);
paint = new Paint(); //設置一個筆刷大小是3的黃色的畫筆
paint.setColor(Color.RED);
}
//在這裏我們將測試canvas提供的繪製圖形方法
@Override
protected void onDraw(Canvas canvas) {
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.STROKE);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
//將要畫的位置移動到屏幕中間
canvas.translate(canvas.getWidth()/2, canvas.getHeight()/2);
//將位置移動畫紙的座標點:150,150
//以半徑爲150和180畫圓
canvas.drawCircle(0, 0, 150, paint);
canvas.drawCircle(0, 0, 180, paint);
//使用path繪製路徑文字
canvas.save();
//移動繪製文字的位置
canvas.translate(0, 0);
Path path = new Path();
//繪製的時候要注意左上不能大於右下,否則不會顯示
RectF rect = new RectF(-100,-100,100,100);
path.addArc(rect, -220, 280);
Paint citePaint = new Paint(paint);
citePaint.setTextSize(28);
//設置畫筆的粗細
citePaint.setStrokeWidth(3);
//float hOffset, float vOffset// 設置水平位置 vOffset 設置垂直位置
// 如果hOffset爲0 說明開始位置在path.addArc設置的startAngle開始角度
// 如果vOffset 爲0說明經過的位置是在與RectF的頂部相切處
canvas.drawTextOnPath("http://blog.csdn.net/u014452224", path, 14, 0, citePaint);
//爲了方便一些轉換操作,Canvas 還提供了保存和回滾屬性的方法(save和restore),
// 比如你可以先保存目前畫紙的位置(save),
// 然後旋轉90度,向下移動100像素後畫一些圖形,畫完後調用restore方法返回到剛纔保存的位置
canvas.restore();
Paint smallPaint = new Paint(paint); //非數字刻度畫筆對象
smallPaint.setStrokeWidth(2);
smallPaint.setColor(Color.GRAY);
float y=150;
int count = 60; //總刻度數
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStrokeWidth(6);
paint.setColor(Color.RED);
paint.setTextSize(24);
paint.setStrokeWidth(3);
for(int i=0 ; i <count ; i++){
if(i%5 == 0){
//繪製數字刻度
canvas.drawText(i == 0 ? "12" : String.valueOf(i / 5),((i / 5)>9||i==0)?-15f:-6f , -y-5f, paint);
}else{
//繪製非數字的刻度
canvas.drawLine(0f, y, 0f, y +15f, smallPaint);
}
canvas.rotate(360/count,0f,0f); //旋轉畫紙
}
//繪製秒針
paint.setColor(Color.RED);
paint.setStrokeWidth(4);
//畫固定的圓圈
canvas.drawCircle(0, 0, 7, paint);
//畫內部固定的點
paint.setStyle(Paint.Style.FILL);
//float cx, float cy, float radius, Paint paint
// cx中心點x座標 cy中心點y座標
canvas.drawCircle(0, 0, 5, paint);
//float startX, float startY, float stopX, float stopY, Paint paint
canvas.drawLine(0, 30, 0, -145, paint);
}
}
}
對上面用到的canvas.save()和canvas.restore()來說明一下。它倆是兩個相互匹配出現的,作用是用來保存畫布的狀態和取出保存的狀態的。 當我們對畫布進行旋轉,縮放,平移等操作的時候其實我們是想對我們指定的元素進行操作,比如圖片,一個矩形等,但是當你用canvas的方法來進行這些操作的時候,其實是對整個畫布進行了操作,那麼操作以後之前在畫布上的元素都會受到影響,所以我們在對畫布進行旋轉,縮放,平移操作之前調用canvas.save()來保存畫布當前的狀態,當操作之後取出之前保存過的狀態,這樣就不會對其他的元素進行影響
我們來看看效果圖就會知道怎麼回事了
示例代碼爲
@Override
protected void onDraw(Canvas canvas) {
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.STROKE);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
canvas.translate(100, 100);
canvas.drawCircle(0, 0, 50, paint);
canvas.restore();
Path path=new Path();
path.moveTo(100,100);
path.lineTo(100,200);
path.lineTo(200,200);
path.lineTo(200,100);
path.close();
canvas.drawPath(path,paint);
canvas.translate(100, 300);
canvas.drawCircle(0, 0, 50, paint);
Path path1=new Path();
path1.moveTo(100,100);
path1.lineTo(100,200);
path1.lineTo(200,200);
path1.lineTo(200,100);
path1.close();
canvas.drawPath(path1,paint);
}
下面的效果圖是在畫布移動之前沒有使用canvas.save()保存狀態和使用
canvas不只是顯示自己畫的,也可以顯示我們的資源圖片,canvas裏面有一個drawBitmap方法,我們可以通過canvas.drawBitmap在我們設置的位置展示圖片
示例代碼
Bitmap bitmap=null;
bitmap=((BitmapDrawable)getResources().getDrawable(R.drawable.ubuntu)).getBitmap();
canvas.drawBitmap(bitmap, 0, 0, null);
bitmap=((BitmapDrawable)getResources().getDrawable(R.drawable.timer)).getBitmap();
canvas.drawBitmap(bitmap, 50, 50, null);
如何讓時鐘轉動起來很簡單,就是在原來的時鐘基礎上面再重新畫出時分秒針。然後設置一個定時器就可以了。
實現代碼如下:
package demo.liuyongxiang.com.demo.activities;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import java.util.Calendar;
import demo.liuyongxiang.com.demo.R;
/**
* Created by ytx on 2016/11/10.
*/
public class MyCanvas extends Activity {
private Paint mPaint;
private float mCenterX;
private float mHourLength;
private float mMinuteLength;
private float mSecondLength;
private Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new CustomView1(MyCanvas.this));
}
class CustomView1 extends View implements Runnable{
Paint paint;
public CustomView1(Context context) {
super(context);
paint = new Paint(); //設置一個筆刷大小是3的黃色的畫筆
paint.setColor(Color.RED);
handler.postDelayed(this, 1000);
}
//在這裏我們將測試canvas提供的繪製圖形方法
@Override
protected void onDraw(Canvas canvas) {
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.STROKE);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
//將要畫的位置移動到屏幕中間
canvas.translate(canvas.getWidth()/2, canvas.getHeight()/2);
//將位置移動畫紙的座標點:150,150
//以半徑爲150和180畫圓
canvas.drawCircle(0, 0, 150, paint);
canvas.drawCircle(0, 0, 180, paint);
//使用path繪製路徑文字
canvas.save();
//移動繪製文字的位置
canvas.translate(0, 0);
Path path = new Path();
//繪製的時候要注意左上不能大於右下,否則不會顯示
RectF rect = new RectF(-100,-100,100,100);
path.addArc(rect, -220, 280);
Paint citePaint = new Paint(paint);
citePaint.setTextSize(28);
//設置畫筆的粗細
citePaint.setStrokeWidth(3);
//float hOffset, float vOffset// 設置水平位置 vOffset 設置垂直位置
// 如果hOffset爲0 說明開始位置在path.addArc設置的startAngle開始角度
// 如果vOffset 爲0說明經過的位置是在與RectF的頂部相切處
canvas.drawTextOnPath("http://blog.csdn.net/u014452224", path, 14, 0, citePaint);
//爲了方便一些轉換操作,Canvas 還提供了保存和回滾屬性的方法(save和restore),
// 比如你可以先保存目前畫紙的位置(save),
// 然後旋轉90度,向下移動100像素後畫一些圖形,畫完後調用restore方法返回到剛纔保存的位置
canvas.restore();
Paint smallPaint = new Paint(paint); //非數字刻度畫筆對象
smallPaint.setStrokeWidth(2);
smallPaint.setColor(Color.GRAY);
float y=150;
int count = 60; //總刻度數
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStrokeWidth(6);
paint.setColor(Color.RED);
paint.setTextSize(24);
paint.setStrokeWidth(3);
for(int i=0 ; i <count ; i++){
if(i%5 == 0){
//繪製數字刻度
canvas.drawText(i == 0 ? "12" : String.valueOf(i / 5),((i / 5)>9||i==0)?-15f:-6f , -y-5f, paint);
}else{
//繪製非數字的刻度
canvas.drawLine(0f, y, 0f, y +15f, smallPaint);
}
canvas.rotate(360/count,0f,0f); //旋轉畫紙
}
Calendar calendar = Calendar.getInstance();
int currentMinute = calendar.get(Calendar.MINUTE);
int currentHour = calendar.get(Calendar.HOUR);
int currentSecond = calendar.get(Calendar.SECOND);
// 計算分針和時間的角度
double secondRadian = Math.toRadians((360 - ((currentSecond * 6) - 90)) % 360);
double minuteRadian = Math.toRadians((360 - ((currentMinute * 6) - 90)) % 360);
double hourRadian = Math.toRadians((360 - ((currentHour * 30) - 90))% 360 - (30 * currentMinute / 60));
// 設置實針爲6個象素粗
paint.setStrokeWidth(6);
// 在錶盤上畫時針
mCenterX = 0;
mHourLength = 100;
canvas.drawLine(mCenterX, mCenterX,
(int) (mCenterX + mHourLength * Math.cos(hourRadian)),
(int) (mCenterX - mHourLength * Math.sin(hourRadian)), paint);
// 設置分針爲4個象素粗
paint.setStrokeWidth(4);
mMinuteLength = 120;
// 在錶盤上畫分針
canvas.drawLine(mCenterX, mCenterX,
(int) (mCenterX + mMinuteLength* Math.cos(minuteRadian)),
(int) (mCenterX - mMinuteLength* Math.sin(minuteRadian)),
paint);
// 設置分針爲2個象素粗
paint.setStrokeWidth(2);
// 在錶盤上畫秒針
mSecondLength = 145;
int centerY = 30;
canvas.drawLine((int) (mCenterX - centerY* Math.cos(secondRadian)),(int) (mCenterX + centerY* Math.sin(secondRadian)),
(int) (mCenterX + mSecondLength* Math.cos(secondRadian)),
(int) (mCenterX - mSecondLength* Math.sin(secondRadian)),
paint);
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(0, 0, 5, paint);
}
@Override
public void run() {
// 重新繪製View
this.invalidate();
// 重新設置定時器,在60秒後調用run方法
handler.postDelayed(this, 1000);
}
}
}
通過效果圖和代碼我們可以知道,我們設置的座標點其實就是我們所要展示的圖片的左上角的頂點。
如果你覺得這篇博客對你有幫助請給予好評,如有疑問請留言。