1. 程式人生 > >Paint 和 Canvas 類常用方法說明

Paint 和 Canvas 類常用方法說明

自定義控制元件是 Android 開發很吸引人的一部分,各種私人定製的效果和各種炫酷的效果都需要通過自定義控制元件來實現,而在自定義控制元件中,最常用的兩個類也就是 Paint (畫筆)類和 Canvas (畫布)類了,所以在這裡記錄一下它們的常用方法,對實現自定義控制元件也會有一定幫助。


1 Paint 類的常用方法

1.1 與 Text 相關的常用方法

public void setTextSize(float textSize):設定繪製文字的大小,必須大於 0。
public void setTextAlign(Align align):設定繪製文字的對齊方向,取值有 Paint.Align.LEFT、Paint.Align.CENTER、Paint.Align.RIGHT。
public void setTextLocale(@NonNull Locale locale):
API 17 出現的,設定地理位置,一般用 Locale.getDefault() 即可,我在學習的時候分別設定了 Locale.CHINESE 、 Locale.ENGLISH 、 Locale.JAPANESE,區別就是字型不太一樣,並不是我想象中的自動翻譯。 public void setTextLocales(@NonNull @Size(min=1) LocaleList locales):API 24 出現的,設定地理位置組。
public void setTextScaleX(float scaleX):設定繪製文字 x 軸的縮放比例,即文字的拉伸效果。預設值為 1,大於 1 則拉伸,大於 0 且小於 1 則收縮,小於 0 的時候會有意想不到的效果。
public void setTextSkewX(float skewX):
設定繪製文字傾斜度,預設值為 0,取值範圍貌似是 -48.0f ~ 48.0f。
public void setLinearText(boolean linearText):設定繪製文字是否需要快取,true 為不需要。
public void setSubpixelText(boolean subpixelText):當設定為 true 時,它可以保證在繪製斜線時平滑該斜線的外觀,相當於繪製文字的抗鋸齒效果。
public void setUnderlineText(boolean underlineText):設定繪製文字是否帶有下劃線。
public void setElegantTextHeight(boolean elegant):
API 21 出現的,設定繪製文字是否具有優雅的文字高度,但是具體有什麼效果還有試出來。
public void setFakeBoldText(boolean fakeBoldText):設定繪製文字是否為粗體字,當字型大小比較小時效果會非常差。
public void setStrikeThruText(boolean strikeThruText):設定繪製文字是否帶有刪除線。
public void setTypeface(Typeface typeface):設定字型,常用字型:DEFAULT 、 DEFAULT_BOLD 、 SANS_SERIF 、 SERIF 、 MONOSPACE。
設定字型程式碼如下,各字型的效果大家可以自己試下:
        Typeface typeface = Typeface.create(Typeface.MONOSPACE, Typeface.NORMAL);
        mPaint.setTypeface(typeface);

1.2 與圖形相關的常用方法

public void setARGB(int a, int r, int g, int b):設定畫筆透明度及顏色,各引數取值範圍都是0~255。
public void setAlpha(int a):設定畫筆透明度,取值範圍為0~255,255為完全不透明。
public void setColor(Color color):設定畫筆顏色。
public void setAntiAlias(boolean aa):設定抗鋸齒,設定為 false 會出現鋸齒狀的邊界,設定為 true 會多消耗效能但邊界就會變得模式,避免鋸齒狀的情況。
public void setDither(boolean dither):防抖動,這個屬性的需求場景主要出現在繪製漸變色彩或含漸變的圖片時,Android 對不含 alpha 通道的圖片會進行一個轉化,成為 RGB565 格式的,這種格式佔用記憶體小,但因為如此,就會出現討厭的“色帶”情景,讓人感覺過渡的不是那麼柔和,針對這個問題,Android 提出了防抖動,設定為 true ,它會將原始顏色的過渡處根據兩邊的色值進行一些改變,從而讓顏色過渡更加的柔和,讓人覺得是平滑的過渡。
public void setStyle(Style style):設定畫筆樣式,可選引數有 Paint.Style.FILL(填充)、 Paint.Style.STROKE(描邊)、 Paint.Style.FILL_AND_STROKE(填充及描邊)。
public void setStrokeCap(Cap cap):設定畫筆帽,可選引數有 Paint.Cap.BUTT(無筆帽)、 Paint.Cap.ROUND(圓形筆帽)、 Paint.Cap.SQUARE(方形筆帽)。筆帽的意思就是前後兩端多出來一截。

public void setStrokeJoin(Join join):這個方法用於設定接合處的形態,就像你用程式碼畫了一條線,但是這條線其實是由無數條小線拼接成的,拼接處的形狀就由該方法指定。可選引數是:Paint.Join.BEVEL(直線)、 Paint.Join.MITER(銳角)、Paint.Join.ROUND(圓弧)。但其實 Paint.Join.Round 和 Paint.Join.BEVEL 並沒有明顯的區別。
public void setStrokeWidth(float width):當畫筆樣式為 STROKE 或 FILL_AND_STROKE 時,設定畫筆的寬度。 public void setFilterBitmap(boolean filter):如果該項設定為 true ,則影象在動畫進行中會濾掉對 Bitmap 影象的優化操作,加快顯示速度,本設定項依賴於 dither 和 xfermode 的設定。 public void setShadowLayer(float radius, float dx, float dy, int shadowColor):在圖形下面設定陰影層,產生陰影效果,radius 為陰影的角度,dx 和 dy 為陰影在 x 軸和 y 軸上的距離,color 為陰影的顏色。 public void setShader(Shader shader):設定影象效果,使用 Shader 可以繪製出各種漸變效果。 public Xfermode setXfermode(Xfermode xfermode):設定圖形重疊時的處理方式,如合併,取交集或並集,經常用來製作橡皮的擦除效果。
public void setMaskFilter(MaskFilter maskfilter):設定 MaskFilter ,可以用不同的 MaskFilter 實現濾鏡的效果,如濾化,立體等。
public void setColorFilter(ColorFilter colorfilter):設定顏色過濾器,可以在繪製顏色時實現不用顏色的變換效果。
public void setPathEffect(PathEffect effect):設定繪製路徑的效果,如點畫線等。
後面的五個方法這是個很強大的方法,我還沒研究透,具體用法在這裡推薦一個大神的系列部落格: Android自定義控制元件三部曲文章索引

其實在自定義控制元件中要想實現很多類似 PhotoShop 處理圖形的效果,大多數是設定 Paint ,而 Canvas 更多是用來顯示圖形的。如果想研究得很深,上面連結推薦的大神的部落格真的寫得很好,我也會向他看齊的。

2 Canvas 類常用方法

Canvas 可以理解為畫布,我們可以在這個畫布上畫點、線、圓、矩形和點陣圖等,我們還可以對這個畫布進行旋轉、平移、縮放等。而 Canvas 大致上可以分為兩類,一種是 View,用來畫普通的影象,這類影象一般計算量比較小,就算含有動畫也是幀率比較低的動畫;另一種是 SurfaceView,用來顯示高品質動畫,一般用在遊戲上,SurfaceView 中專門有一個執行緒來完成畫圖工作,所以程式不需要等待 View 完成畫圖,提高效能。 除了知道 Canvas 的分類之外,還需要了解的一點是 Canvas 牽扯到兩種座標系,一種是 Canvas 座標系,它指的是 Canvas 本身的座標系,它是唯一且不變的,座標原點在 View 的左上角,從座標原點向右為 x 軸正半軸,向下為 y 軸正半軸;另一種是繪圖座標系,一般來說這是對我們畫圖真正有用的座標系,因為 Canvas 中的 drawXXXX 方法傳入的各種座標都是繪圖座標系中的座標,預設情況下它是和 Canvas 座標系完全重合,也就是說初始情況下它的座標原點也在 View 的左上角,從座標原點向右為 x 軸正半軸,向下為 y 軸正半軸,但是我們可以通過平移、旋轉、縮放等方法來改變繪圖座標系,而且這些操作都是基於當前繪圖座標系的,繪圖也是基於當前座標系的,這麼說可能比較抽象,一會兒會在 2.1 用程式碼說明繪圖座標系的變化。

2.1 對 Canvas 的操作方法

先舉個栗子,我們隨便寫一個自定義控制元件,在 onDraw() 方法中對 Canvas 進行一些操作:
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //先畫一條線,以這條線為參照物
        mPaint.setColor(Color.RED);
        canvas.drawLine(0, 100, 800, 100, mPaint);

        //Canvas 的 x 軸平移 100dx,y 軸平移 100dx.
        canvas.translate(100, 100);
        mPaint.setColor(Color.YELLOW);
        canvas.drawLine(0, 100, 800, 100, mPaint);

        //Canvas 順時針旋轉 30 度.
        canvas.rotate(30);
        mPaint.setColor(Color.BLUE);
        canvas.drawLine(0, 100, 800, 100, mPaint);

        //Canvas 縮放 2 倍
        canvas.scale(2,2);
        mPaint.setColor(Color.GREEN);
        canvas.drawLine(0, 100, 800, 100, mPaint);
    }

執行程式後,結果如下:

我們每次在 drawLine() 方法中傳入的 startX、startY、stopX、StopY 都是一樣的,但是畫出來的線卻在不同的位置,就是因為上面提到座標系分為 Canvas 座標系和繪圖座標系,對 Canvas 進行平移、旋轉、縮放等操作是可以改變繪圖座標系的,而繪圖又是基於繪圖座標系的,所以雖然我們傳入的 startX、startY、stopX、StopY 都是一樣的,但是由於參考的繪圖座標系改變了,所以畫出來的線也不一樣了。
public void translate(float dx, float dy):平移畫布,dx 為在 x 軸上平移的距離,dy 為在 y 軸上平移的距離。
public final void scale(float sx, float sy):縮放畫布,sx 為 x 軸縮放的倍數,sy 為 y 軸縮放的倍數。
public final void scale(float sx, float sy, float px, float py):縮放畫布,sx、sy 同上,px 為 x 軸的基準點,py 為 y 軸的基準點,預設情況是以原點為基準點,這個方法可以以某個中心點來進行縮放。
public void rotate(float degrees):旋轉畫布,degrees 為旋轉的角度,正值為順時針旋轉,負值為逆時針旋轉。
public final void rotate(float degrees, float px, float py):類似 scale() 方法,可以指定基準點。
public void skew(float sx, float sy):傾斜畫布,sx 為將畫布在 x 軸上傾斜相應的角度的 tan 值,sy 為將畫布在 y 軸上傾斜相應的角度的 tan 值。

關於 scale() 和 rotate() 提到的基準點的問題,用一段程式碼來說明一下:
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //先畫一條線,以這條線為參照物
        mPaint.setColor(Color.RED);
        canvas.drawLine(0, 100, 800, 100, mPaint);

        //預設原點為基準點縮放 0.5 倍
        mPaint.setColor(Color.YELLOW);
        canvas.scale(0.5f, 0.5f);
        canvas.drawLine(0, 100, 800, 100, mPaint);

        //指定基準點縮放 0.5 倍
        canvas.restore();
        mPaint.setColor(Color.BLUE);
        canvas.scale(0.5f, 0.5f, 400, 50);
        canvas.drawLine(0, 100, 800, 100, mPaint);
    }

執行程式後,結果如下:
可以看到我們知道的基準點不同,所以縮放後的中心點也不同。rotate() 方法同理。 在上面的程式碼中 Canvas 呼叫了一個 restore() 方法,事實上 Canvas 是可以儲存當前座標系的狀態的,也可以進行還原。主要涉及下面幾個方法: public int save():儲存畫布當前狀態。
public int save(@Saveflags int saveFlags):儲存畫布當前狀態到指定位置。
public void restore():恢復畫布狀態至上一次儲存的狀態。
public int getSaveCount():返回畫布儲存的狀態個數。
public void restoreToCount(int saveCount):恢復畫布狀態至指定位置的狀態。

2.2 利用 Canvas 繪製的方法

剛才說了可以在 Canvas 上繪製點、線、圓、矩形等等圖形,下面就詳細解釋一下各個圖形怎麼畫。

2.2.1 填充畫布

public void drawRGB(int r, int g, int b):使用 RGB 填充畫布。
public void drawARGB(int a, int r, int g, int b):使用 ARGB 填充畫布。
public void drawColor(@ColorInt int color):使用 Color 填充畫布。
public void drawPaint(@NonNull Paint paint):使用指定 paint 填充畫布。

2.2.2 畫點

public void drawPoints(@Size(multiple=2) @NonNull float[] pts, @NonNull Paint paint):畫多個點,傳入座標陣列,陣列中每兩個值為一組,為一個點的座標,如果最後不足兩個則忽略。
public void drawPoints(@Size(multiple=2) float[] pts, int offset, int count, @NonNull Paint paint):畫多個點,傳入座標陣列,陣列中每兩個值為一組,為一個點的座標,如果最後不足兩個,則忽略,offset 為擷取的取值範圍起始位置(包含),count 為擷取的取值範圍的結束位置(包含)。
public void drawPoint(float x, float y, @NonNull Paint paint):畫一個點,x 為 x 軸座標,y 為 y 軸座標。 示例程式碼:
        mPaint.setColor(Color.RED);
        canvas.drawPoint(800, 800, mPaint);
        mPaint.setColor(Color.YELLOW);
        canvas.drawPoints(new float[]{100, 100, 200, 200, 300, 300, 400, 400, 500, 500, 600, 600}, mPaint);
        mPaint.setColor(Color.BLUE);
        canvas.drawPoints(new float[]{100, 100, 200, 200, 300, 300, 400, 400, 500, 500, 600, 600}, 1, 5, mPaint);


2.2.3 畫線

public void drawLine(float startX, float startY, float stopX, float stopY, @NonNull Paint paint):畫一條線,startX 為 x 軸起點座標,startY 為 y 軸起點座標,stopX 為 x 軸終點座標,stopY 為 y 軸終點座標。
public void drawLines(@Size(multiple=4) @NonNull float[] pts, @NonNull Paint paint):畫多條線,同 public void drawPoints(@Size(multiple=2) @NonNull float[] pts, @NonNull Paint paint),不過陣列中是每四個值為一組,如果最後不足四個則忽略。
public void drawLines(@Size(multiple=4) @NonNull float[] pts, int offset, int count, @NonNull Paint paint):畫多條線,同 public void drawPoints(@Size(multiple=2) float[] pts, int offset, int count, @NonNull Paint paint),不過陣列中是每四個值為一組,如果最後不足四個則忽略。 示例程式碼:
        mPaint.setColor(Color.RED);
        canvas.drawLine(100, 500, 500, 500, mPaint);
        mPaint.setColor(Color.YELLOW);
        canvas.drawLines(new float[]{100, 600, 500, 600, 100, 700, 500, 700, 100, 800, 500, 800}, mPaint);
        mPaint.setColor(Color.BLUE);
        canvas.drawLines(new float[]{100, 900, 500, 900, 100, 1000, 500, 1000, 100, 1100, 500, 1100}, 1, 9, mPaint);

2.2.4 畫矩形

public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint):畫矩形,引數分別為矩形的左邊、頂邊、右邊和底邊。
public void drawRect(@NonNull Rect r, @NonNull Paint paint):畫矩形,r 為 int 型別引數建立的矩形。
public void drawRect(@NonNull RectF rect, @NonNull Paint paint):畫矩形,rect 為 float 型別引數建立的矩形。
示例程式碼:
        mPaint.setColor(Color.RED);
        canvas.drawRect(30, 30, 800, 500, mPaint);
        mPaint.setColor(Color.YELLOW);
        canvas.drawRect(new Rect(30, 600, 800, 1100), mPaint);
        mPaint.setColor(Color.BLUE);
        canvas.drawRect(new RectF(30, 1200, 800, 1700), mPaint);

2.2.5 畫橢圓

public void drawOval(float left, float top, float right, float bottom, @NonNull Paint paint):API 21 出現的,畫橢圓,引數分別為矩形的左邊、頂邊、右邊和底邊。
public void drawOval(@NonNull RectF oval, @NonNull Paint paint):畫橢圓,oval 為 float 型別引數建立的矩形。 示例程式碼:
        mPaint.setColor(Color.RED);
        canvas.drawOval(30, 30, 800, 600, mPaint);
        mPaint.setColor(Color.YELLOW);
        canvas.drawOval(new RectF(30, 700, 800, 1300), mPaint);


2.2.6 畫圓

public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint):畫圓,cx 為圓心在 x 軸的座標,cy 為圓心在 y 軸的座標,radius 為圓的半徑。 示例程式碼:
        mPaint.setColor(Color.RED);
	canvas.drawCircle(500, 500, 300, mPaint);


2.2.7 畫扇形或弧線

public void drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint):API 21 出現的,畫扇形或者弧線,引數分別為矩形的左邊、頂邊、右邊、底邊,開始角度,扇形的角度,是否需要和圓心連線。
public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint):oval 為 float 型別引數建立的矩形,其餘引數同上。
示例程式碼:
        mPaint.setColor(Color.RED);
        canvas.drawArc(30, 30, 500, 600, 0, 135, true, mPaint);
        mPaint.setColor(Color.YELLOW);
        mPaint.setStyle(Paint.Style.STROKE);
        canvas.drawArc(new RectF(30, 700, 500, 1300), 0, 135, false, mPaint);


2.2.8 畫圓角矩形

public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, @NonNull Paint paint):API 21 出現的,引數分別為矩形的左邊、頂邊、右邊、底邊,橢圓的位於 x 軸的圓角的半徑,橢圓的位於 y 軸的圓角的半徑。
public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint):rect 為 float 型別引數建立的矩形。 示例程式碼:
        mPaint.setColor(Color.RED);
        canvas.drawRoundRect(30, 30, 800, 600, 45, 45, mPaint);
        mPaint.setColor(Color.YELLOW);
        canvas.drawRoundRect(new RectF(30, 700, 800, 1300), 30, 50, mPaint);


2.2.9 畫路線

public void drawPath(@NonNull Path path, @NonNull Paint paint):畫路線,也可以利用該方法來畫任意多邊形,引數 path 為路線物件。
示例程式碼:
        mPaint.setColor(Color.RED);
        mPaint.setStyle(Paint.Style.STROKE);
        Path path = new Path();
        path.moveTo(50, 100);
        path.lineTo(50, 300);
        path.lineTo(100, 500);
        path.lineTo(400, 500);
        path.lineTo(300, 300);
        path.lineTo(450, 50);
        path.lineTo(200, 200);
        path.lineTo(50,100);
        canvas.drawPath(path, mPaint);

2.3 小結

Canvas 還提供了畫圖片和畫文字的方法,畫文字如果結合 1.1 中的 Paint 的不同設定,可以產生更多的效果,上面的畫各種圖形的方法也是一樣,如果畫筆設定不同,畫出來的圖形也有不同的效果。至於畫圖片,這其中的細節很多,不在這裡多說,今後會慢慢研究怎麼實現那些濾鏡效果。

3 總結

自定義控制元件是 Android 最吸引人的地方之一,可以說自定義控制元件真的是可以實現任何效果,而如果想學好自定義控制元件,Paint 和 Canvas 只是其中一部分,這一部分的東西掌握個基本還是不難,如果是專門做圖片處理的話可能就需要好好研究一下了。這篇部落格寫了很久,因為雖然是看到別的大神的部落格才想去學習這兩個類,但是這跟視覺效果有關的東西,想看看每一個方法設定不同的引數都有什麼效果所以很多方法都自己試了試,學習完之後也有一種感覺就是之前看到的很多 github 上開源的自定義控制元件如果要自己實現的話應該會有一些頭緒了。路很長,水很深,為了更美好的明天需要多努力。