Android Canvas、Paint、Path、drawBitmap

我們平常的畫圖都是通過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);
        }
    }

}

通過效果圖和代碼我們可以知道,我們設置的座標點其實就是我們所要展示的圖片的左上角的頂點。
如果你覺得這篇博客對你有幫助請給予好評,如有疑問請留言。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章