Graphics2D API:Paint類、Canvas類

現實生活中作畫,有兩樣東西必不可少:筆、紙,Android中圖形繪制類似于此.
在Android中:
Paint類就是我們的“畫筆”,為繪圖定義各種參數:顏色、文本樣式、圖形樣式等等.
Canvas類就是我們的“畫布”,Canvas類中提供了若干方法用于繪制各種圖案:點、線、矩形、圓等等.
掌握Graphics繪圖是自定義組件的基礎,Android給了我們一支筆(Paint)和一張紙(Canvas),畫出什么樣的圖形取決于我們的想象力以及對Graphics繪圖的掌握程度.

一、Paint類

Paint類就是畫筆,用來設置繪圖時的參數,比如:筆的顏色、筆的粗細等等.
使用方法很簡單,首先定義一個畫筆對象:

Paint mPaint = new Paint();

然后就可以調用Paint以set開頭的相關方法為畫筆設置各種參數了:

mPaint.set......

最后,在畫布Canvas調用相關繪圖方法的時候把畫筆對象Paint傳進去就可以了:

canvas.draw......(其它參數.....,mPaint);

這里先簡單介紹畫筆的幾個方法,后面會詳細介紹畫筆Paint.

1、畫筆顏色設置

畫筆肯定有顏色了,Paint設置顏色的方法有下面3個,我們一般使用的是第1個

public void setColor(int color)  設置顏色值
public void setAlpha(int a)  設置透明度
public void setARGB(int a, int r, int g, int b)  指定透明度、紅、綠、藍定義一種顏色
2、畫筆填充樣式設置
public void setStyle(Style style)
設置畫筆填充樣式,可選值:
    Paint.Style.FILL  填充內部(默認值)
    Paint.Style.STROKE  僅描邊
    Paint.Style.FILL_AND_STROKE 填充內部和描邊

上圖是調用Canvas畫了一個正方形在Paint三種填充樣式下的效果,FILL就是內部填充滿畫筆的顏色,STROKE就是圖形是空心的,在外層描邊,所以STROKE看著比FILL大一點,FILL_AND_STROKE就是兩者的綜合.

3、畫筆寬度、抗鋸齒功能
public void setStrokeWidth(float width)  設置畫筆寬度

當畫筆填充樣式為STROKE 、FILL_AND_STROKE 的時候,我們可以設置畫筆的寬度,畫筆寬度越大,描的邊就越“厚”.

public void setAntiAlias(boolean aa)
aa為true時開啟抗鋸齒功能

我們畫一條直線,細看是直的,但是仔細看會發現直線上有很多鋸齒,就像電鋸一樣,這個方法可以讓繪圖過程中的線條更加平滑.

4、文本樣式設置

Canvas是可以繪制文字的,文字的顏色就是Paint設置的顏色,Paint還可以為文字設置下面這些樣式:

public void setTextSize(float textSize)
設置文本字體大小,單位px

public void setTextAlign(Align align)
設置文本對齊方式,可選值:Paint.Align.LEFT、Paint.Align.CENTER、Paint.Align.RIGHT
繪制文本時會有個起始點坐標,三個值分別代表:在起始坐標的左邊繪制文本、以起始坐標為中心繪制文本、在起始坐標的右邊繪制文本

public void setTextScaleX(float scaleX)
將文本沿X軸水平縮放,默認值為1,大于1時會沿X軸水平放大文本,小于1會沿X軸水平縮放文本

public void setTextSkewX(float skewX)
設置文本傾斜程度,范圍:-1~1,正負表示傾斜的方向,默認0不傾斜

public void setUnderlineText(boolean underlineText)
給文本添加下劃線,true添加下劃線

public void setFakeBoldText(boolean fakeBoldText)
文本粗體設置,true表示設置為粗體

public void setStrikeThruText(boolean strikeThruText)
文本添加刪除線,true表示添加刪除線
5、copy畫筆、重置畫筆
public void set(Paint src)
為當前畫筆設置一個畫筆
在繪圖時,有時我們會用到不止一種畫筆,如果有個畫筆的屬性都設置好了,另外一個畫筆也需要這些屬性,就可以直接copy過來

public void reset() 
重置畫筆,恢復初始狀態

二、Canvas類

Canvas類提供了大量以draw...開頭的方法用于繪圖.
一般在繪圖前,先創建Paint對象,定義繪制的顏色、樣式等參數. 因為Paint對象可以重置(reset),所以除非有必要,Paint對象創建一個就可以了,然后再調用Canvas的繪圖方法.

Canvas繪圖方法主要有下面幾種類型:
顏色


矩形

路徑
文字
位圖
Canvas操作

1、前置

在講繪圖方法前,我們先自定義一個簡單的XView繼承自View,不然光介紹API不是很好理解.

public class XView extends View {

    private Paint mPaint;

    public XView(Context context) {
        this(context, null);
    }

    public XView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint();//初始化畫筆
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        gogogo(canvas);
    }

    /**
     * 后面所有測試代碼都在gogogo中
     */
    private void gogogo(Canvas canvas){
        ...
    }
}

然后在布局文件中使用:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.fgq.demo.XView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</FrameLayout>

自定義的View在布局文件中寫法:完整包名+類名
2、繪制顏色
public void drawRGB(int r, int g, int b)
public void drawARGB(int a, int r, int g, int b)
public void drawColor(int color)

這里繪制的是整個畫布的顏色

測試:

    private void gogogo(Canvas canvas){
        canvas.drawColor(Color.GRAY);
    }
3、繪制點

雖然是點,但并不表面它很小,點的大小取決于畫筆寬度,也就是Paint.setStrokeWidth(...)參數的大小.
關于點,請看Point類、PointF類.

public void drawPoint(float x, float y, Paint paint)
在(x,y)處繪制一個點

public void drawPoints(float[] pts, Paint paint)
連續繪制多個點:pts是一個數組,從下標0開始,每2個數組元素確定一個點,數組長度必須是2的倍數

public void drawPoints(float[] pts, int offset, int count,Paint paint)
連續繪制多個點:pts是一個數組,從下標offset開始取數組元素,每2個數組元素確定一個點,一共取count個元素

測試:

    private void gogogo(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(20f);
        mPaint.setColor(Color.BLUE);
        canvas.drawPoint(50, 50, mPaint);//點(50,50)

        mPaint.setStrokeWidth(30f);
        mPaint.setColor(Color.RED);
        canvas.drawPoints(new float[]{50, 100, 110, 100, 188, 100}, mPaint);//點(50,100)、(110,100)、(188,100)

        mPaint.setColor(Color.BLACK);
        canvas.drawPoints(new float[]{50, 150, 110, 150, 188, 150}, 2, 4, mPaint);//點(110,150)、(188,150)
4、繪制線段

兩個點確定一條線段,所以,繪制一條線段時需要兩個點的坐標,線段的粗細由畫筆寬度決定

public void drawLine(float startX,float startY,float stopX,float stopY,Paint paint)
在(startX,startY)和(stopX,stopY)   兩個點之間繪制一條線段   

public void drawLines(float[] pts, Paint paint)
繪制多條線段:pts是一個數組,從下標0開始,每4個數組元素確定一條線段,數組長度必須是4的倍數

public void drawLines(float[] pts, int offset, int count,Paint paint)
繪制多條線段:pts是一個數組,從下標offset開始取數組元素,每4個數組元素確定一條線段,一共取count個元素,數組長度必須是4的倍數

測試:

    private void gogogo(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mPaint.setStrokeCap(Paint.Cap.ROUND);//圓形筆觸
        mPaint.setStrokeWidth(20f);
        mPaint.setColor(Color.BLUE);
        canvas.drawLine(50, 50, 500, 50, mPaint);//點(50,50)到(500,50)的線段

        mPaint.setColor(Color.RED);
        canvas.drawLines(new float[]{50, 50, 50, 500, 500, 50, 500, 500}, mPaint);//點(50,50)到(50,500)的線段、點(500,50)到(500,500)的線段

        mPaint.setColor(Color.BLACK);
        canvas.drawLines(new float[]{50, 50, 50, 500, 500, 500, 0, 0}, 2, 4, mPaint);//點(50,50)到(500,500)的線段
    }
5、繪制矩形

矩形分為:直角矩形、圓角矩形.(正方形也是矩形的一種)
關于矩形,請看Rect類、RectF類.
直角矩形:

public void drawRect(float left, float top, float right, float bottom, Paint paint)

public void drawRect(Rect r, Paint paint) 

public void drawRect(RectF rect, Paint paint)

這三個重載方法繪制矩形的本質是完全一樣的,使用哪一個隨意

測試:

    private void gogogo(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(20f);
        mPaint.setColor(Color.BLUE);

        canvas.drawRect(50, 50, 200, 200, mPaint);

        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(Color.RED);
        canvas.drawRect(new Rect(50,250,200,400),mPaint);

        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mPaint.setColor(Color.BLACK);
        canvas.drawRect(new RectF(50f,450f,200f,600f),mPaint);
    }

圓角矩形:

public void drawRoundRect(RectF rect, float rx, float ry, Paint paint)

public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,Paint paint)

相對于直角矩形,圓角矩形多了兩個參數:rx、ry
這兩個參數用來指定4個角的弧度,分別是圓角處的水平半徑、豎直半徑
若rx=ry,則4個拐角是半徑為rx的圓的1/4圓弧,
若rx≠ry,則4個拐角是長半軸、短半軸為rx、ry的橢圓的1/4圓弧.

這二個重載方法繪制矩形的本質是完全一樣的,不同的是第二個需要在>=5.0(API 21)的版本使用

測試:

    private void gogogo(Canvas canvas) {
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(5);
        mPaint.setColor(Color.BLUE);
        canvas.drawRoundRect(50, 50, 600, 600, 20, 40, mPaint);

        mPaint.setColor(Color.RED);
        canvas.drawRoundRect(new RectF(100, 100, 400, 400), 30, 30, mPaint);
    }
6、繪制圓

我把橢圓、圓、弧、扇形統一歸類到“圓”中.
(1)橢圓
橢圓的大小由它的外切矩形決定.

public void drawOval(RectF oval, Paint paint)

public void drawOval(float left, float top, float right, float bottom, Paint paint)

這兩個重載方法本質一樣,都是通過定義橢圓的外切矩形來繪制橢圓
(第二個重載方法需要在>=5.0(API 21)的版本使用)

(2)圓
圓的大小由圓心、半徑決定.

public void drawCircle(float cx, float cy, float radius, Paint paint)
圓心:(cx,cy)      半徑:radius

(3)弧、扇形
弧和扇形相似,都是橢圓上一部分,而橢圓又是由它的外切矩形決定

public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,Paint paint)

public void drawArc(float left, float top, float right, float bottom, float startAngle,float sweepAngle, boolean useCenter,Paint paint)

startAngle:開始角度
sweepAngle:弧線、扇形所占角度,正數順時針,負數逆時針
useCenter:是否使用中心點,true表示扇形、false表示弧線
(第二個重載方法需要在>=5.0(API 21)的版本使用)

當矩形長、寬一樣時,就是正方形,橢圓就變成圓,弧線、扇形就是圓上一部分了

測試:

    private void gogogo(Canvas canvas) {
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(5);
        mPaint.setColor(Color.BLUE);
        RectF rectF = new RectF(50, 50, 600, 400);
        canvas.drawOval(rectF, mPaint);//橢圓

        mPaint.setColor(Color.RED);
        canvas.drawCircle(325, 225, 175, mPaint);//圓

        mPaint.setColor(Color.YELLOW);
        canvas.drawArc(rectF, 90, 90, false, mPaint);//弧

        mPaint.setStyle(Paint.Style.FILL);
        canvas.drawArc(rectF, 180, 90, false, mPaint);//弧

        canvas.drawArc(rectF, 30, 30, true, mPaint);//扇形
        canvas.drawArc(rectF, -30, 30, true, mPaint);//扇形
    }
7、繪制文字

(1)從指定位置開始繪制文字:

public void drawText(String text, float x, float y, Paint paint)

public void drawText(char[] text, int index, int count, float x, float y,Paint paint)
            
public void drawText(String text, int start, int end, float x, float y,Paint paint)
            
public void drawText(CharSequence text, int start, int end, float x, float y,
            Paint paint)

文字內容:text
部分內容:index、count------從下標index開始取,一共取count個數組元素;start、end------從字符串索引start到end處的部分字符串

測試:

    private void gogogo(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(Color.BLUE);
        mPaint.setTextSize(40);//設置字體大小

        String text = "隨風飄揚的Smile";

        canvas.drawText(text, 50, 50, mPaint);//繪制文字

        mPaint.setTextSkewX(-0.5f);//向左傾斜
        canvas.drawText(text, 50, 100, mPaint);
        mPaint.setTextSkewX(0.5f);//向右傾斜
        canvas.drawText(text, 50, 150, mPaint);

        mPaint.setUnderlineText(true);//下劃線
        canvas.drawText(text.toCharArray(), 1, 2, 50, 200, mPaint);

        mPaint.setFakeBoldText(true);//粗體
        mPaint.setStrikeThruText(true);//刪除線
        canvas.drawText(text, 2, text.length() - 1, 50, 250, mPaint);

        mPaint.setTextScaleX(1.5f);//水平放大文本
        canvas.drawText(text, 50, 300, mPaint);
    }

(2)沿著Path路徑繪制文字:
關于Path,請看Path類基本操作.

public void drawTextOnPath(String text, Path path, float hOffset,
            float vOffset, Paint paint)

public void drawTextOnPath(char[] text, int index, int count,Path path,
            float hOffset, float vOffset, Paint paint)

hOffset:文字與路徑起始點的水平偏移距離
vOffset:文字與路徑豎直方向偏移量,>0往Path下方偏移,<0往Path上方偏移
同樣支持截取部分內容:從index處取元素,一共取count個元素

測試:

    private void gogogo(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(5);
        mPaint.setColor(Color.BLUE);
        mPaint.setTextSize(30);

        String text = "隨風飄揚的Smile";

        Path path = new Path();
        path.moveTo(50, 200);
        path.lineTo(666, 666);//線段
        //沿著Path繪制文字
        canvas.drawTextOnPath(text+"1", path, 0, 0, mPaint);
        canvas.drawTextOnPath(text+"2", path, 0, -30, mPaint);
        canvas.drawTextOnPath(text+"3", path, 300, 0, mPaint);
        canvas.drawTextOnPath(text+"4", path, 0, 30, mPaint);
        canvas.drawTextOnPath(text+"5", path, 300, 30, mPaint);

        Path path2 = new Path();
        path2.addCircle(300,300,200, Path.Direction.CCW);//逆向圓,文字沿著逆向
        canvas.drawTextOnPath(text, path2, 0, 0, mPaint);//沿著Path繪制文字

        mPaint.setColor(Color.RED);
        mPaint.setStyle(Paint.Style.STROKE);
        canvas.drawPath(path, mPaint);//繪制Path---線段
        canvas.drawPath(path2, mPaint);//繪制Path---圓
    }

(3)指定文字位置:

public void drawPosText(String text, float[] pos,Paint paint)

public void drawPosText(char[] text, int index, int count,float[] pos,Paint paint)

text:文子內容
pos:每個文字的坐標位置,一個坐標需要2個參數,所以數組pos長度必須是2的倍數
index:第一個文字索引
count:一共繪制count個文字

測試

    private void gogogo(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(Color.BLUE);
        mPaint.setAntiAlias(true); //使用抗鋸齒功能
        mPaint.setTextSize(50);

        float[] textPos = new float[]{10, 50,
                20, 100,
                30, 150,
                40, 200,
                50, 250,
                60, 300,
        };
        canvas.drawPosText("隨風飄揚的笑", textPos, mPaint);
    }
8、繪制位圖
public void drawBitmap(Bitmap bitmap, float left, float top,Paint paint)
將bitmap繪制在畫布上,同時指定位圖相左上角位置(left,top),bitmap大小與原圖一樣,不進行縮放

public void drawBitmap(Bitmap bitmap, Rect src, Rect dst,Paint paint)
public void drawBitmap(Bitmap bitmap, Rect src, RectF dst,Paint paint)
從bitmap中摳出大小為src的矩形區域繪制到畫布dst矩形處,bitmap會縮放適應src區域,所以src與dst的大小與比例關系影響最終繪制效果
若src為null,就將原bitmap繪制到dst處,bitmap會縮放適應src區域

測試

    private void gogogo(Canvas canvas) {
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
        canvas.drawBitmap(bitmap, 50, 50, mPaint);
        canvas.drawBitmap(bitmap, new Rect(0, 0, 30, 30), new Rect(100, 100, 400, 400), mPaint);
    }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,836評論 6 540
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,275評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 177,904評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,633評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,368評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,736評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,740評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,919評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,481評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,235評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,427評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,968評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,656評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,055評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,348評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,160評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,380評論 2 379

推薦閱讀更多精彩內容