Android 自定義View學(xué)習(xí)(七)——Canvas知識學(xué)習(xí)

上篇的學(xué)習(xí),Paint中的基礎(chǔ)知識基本結(jié)束,筆學(xué)完了,學(xué)習(xí)開始學(xué)習(xí)畫布。本篇記錄學(xué)習(xí)Canvas中的一些知識。在drawBitmap()方法中會引出計(jì)劃下篇要學(xué)習(xí)的內(nèi)容Matrix,drawPath()方法會引出計(jì)劃在下下篇學(xué)習(xí)的貝塞爾曲線,學(xué)習(xí)的重點(diǎn)也是這兩個(gè)方法

學(xué)習(xí)資料:

本人很菜,有錯(cuò)誤,請指出


雖然稱Canvas為畫布,但并不是直接在Canvas畫,Canvas內(nèi)部默認(rèn)會創(chuàng)建一個(gè)Biatmap,也可以通過構(gòu)造方法或者setBitmap()方法傳入一個(gè),像素所有的信息是畫在了這個(gè)Bitmap上,然后Bitmap被保存在了Canvas之內(nèi)

這是我看愛哥的博客后,自己做的一個(gè)總結(jié),若有錯(cuò)誤,請指出。針對Canvas的兩種構(gòu)造方法,愛哥針對源碼有做分析,可以看看了解一下

Canvas的方法中,clipdraw方法占據(jù)了一大半,在Android 自定義View學(xué)習(xí)(二)——開始了解Canvas和Paint了解過了幾個(gè)draw方法,本篇進(jìn)行補(bǔ)充學(xué)習(xí)


1.draw方法補(bǔ)充學(xué)習(xí)

draw一系列方法中,有一個(gè)特殊的牛B的存在,drawBitmapMesh(),這個(gè)方法牛B在可以幾乎對Bitmap做任何操作。雖然這個(gè)方法很強(qiáng)大,但使用的頻率并不算高,也有點(diǎn)雞肋。一些比較簡單的Bitmap處理可能優(yōu)先考慮Matrix,而過于復(fù)雜的處理,會耗時(shí)比較久,效率可能并不高。一般優(yōu)先不考慮這個(gè)方法,遇到Matrix實(shí)現(xiàn)不了的需求,記得有這么一個(gè)方法可以學(xué)習(xí),然后使用


1.1 drawBitmap方法

感覺這個(gè)方法使用頻率很高。一共有6個(gè)重載方法,其中兩個(gè)參數(shù)最多的方法已經(jīng)廢棄,也就是需要學(xué)習(xí)4個(gè)

1.1.1 第1個(gè)方法

drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint)
  • bitmap 要畫的目標(biāo)Bitmap
  • left 左上角的X軸坐標(biāo)
  • top 左上角的Y軸坐標(biāo)
  • paint 畫筆

簡單使用

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    final Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);
    final float x = getWidth() / 2 - bitmap.getWidth() / 2;//水平居中
    final float y = 0;
    canvas.drawBitmap(bitmap, x, y, mPaint);
}
drawBitmap第1個(gè)方法

在實(shí)際開發(fā)中,還會考慮圖片寬高的壓縮,顯示位置,以及padding的等等


1.1.2 第2個(gè)和第3個(gè)方法

drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull Rect dst,@Nullable Paint paint)

drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst,@Nullable Paint paint)

兩個(gè)方法的差別在于第三個(gè)參數(shù)RectRectF

  • src 用來截取Bitmap局部所想要顯示的像素塊區(qū)域,通過構(gòu)造方法中的四個(gè)坐標(biāo)系點(diǎn)確定范圍。這個(gè)參數(shù)可以為null,為null就是整個(gè)Bitmap都作為目標(biāo)資源顯示
  • dst 用來顯示的區(qū)域,在控件中繪制Bitmap的區(qū)域,可以實(shí)現(xiàn)拉伸或者縮放

簡單使用

private void init() {
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.test);
    rect = new Rect(0,0,1080,600);
    rectF = new RectF(0f,0f,1080f,600f);
}

protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if (bitmap != null)
        canvas.drawBitmap(bitmap,null,rectF,mPaint);
}
drawBitmap第2個(gè)方法

1080是測試手機(jī)的屏幕寬度

第2個(gè)參數(shù)rect = new Rect(x,y,x1,y1) ,構(gòu)造方法中四個(gè)參數(shù)分別為:

  • x, X軸開始的坐標(biāo)點(diǎn),默認(rèn)為0
  • y, Y軸開始的坐標(biāo)點(diǎn)
  • x1, X軸結(jié)束的坐標(biāo)點(diǎn),若x1-x大于了Bitmap的寬度,截取的有效區(qū)域就是Bitmap的寬度
  • y1, Y軸結(jié)束的坐標(biāo)點(diǎn)

第3個(gè)參數(shù)rectF = new RectF(x,y,x1,y1),構(gòu)造方法中四個(gè)參數(shù)分別為:

  • x,開始繪制的X軸的坐標(biāo)點(diǎn),默認(rèn)為0
  • y,開始繪制的Y軸的坐標(biāo)點(diǎn)
  • x1,結(jié)束繪制的X軸的坐標(biāo)點(diǎn),若x1-x大于srcx1-x,就是拉伸;小于就是縮放
  • y1,結(jié)束繪制的Y軸的坐標(biāo)點(diǎn)

簡單修改代碼:

rect = new Rect(0,0,400,600);
rectF = new RectF(100f,0f,700f,600f);
拉伸顯示局部

此時(shí)在控件X100f位置開始繪制資源Bitmap(0,0)到(400,600)局部區(qū)域,最終的顯示效果就成了拉伸局部的效果。Y軸同理

這兩個(gè)參數(shù)的作用,用幾個(gè)數(shù)簡單測試一下,比較直觀。


1.1.3 第4個(gè)方法

drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint)

簡單使用

private void init() {
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);
    matrix = new Matrix();
    matrix.setTranslate(100f,100f);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawBitmap(bitmap,matrix,mPaint);
}
drawBitmap第4個(gè)方法

Matrix是一個(gè)3 * 3的矩陣,下篇進(jìn)行記錄學(xué)習(xí)


1.2 畫Line方法

畫一條線的方法

drawLine(float startX, float startY, float stopX, float stopY, @NonNull Paint paint)

畫多條線的方法

drawLines(@Size(multiple=4) @NonNull float[] pts, @NonNull Paint paint)

drawLines(@Size(multiple=4) @NonNull float[] pts, int offset, int count,@NonNull Paint paint)

1.2.1 drawLine 畫一條線

drawLine()方法就一個(gè),沒有重載方法。畫規(guī)則曲線可以考慮使用Path

private void init() {
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setColor(Color.parseColor("#FF4081"));
    mPaint.setStrokeWidth(10f);
}

protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawLine(100f,100f,600f,500f,mPaint);
}
畫直線

方法參數(shù)也很簡單,四個(gè)參數(shù)確定兩個(gè)點(diǎn)的坐標(biāo),然后兩點(diǎn)一線


1.2.2 drawLines畫多條直線

兩個(gè)參數(shù)的drawsLines(float[] pts, Paint paint)方法簡單使用

private void init() {
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setColor(Color.parseColor("#FF4081"));
    mPaint.setStrokeWidth(10f);
    floats = new float[]{100f,100f,300f,300f,400f,200f,600f,200f};//每4個(gè)數(shù)一組,確定兩個(gè)點(diǎn)的坐標(biāo)
}

protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawLines(floats,mPaint);
}
畫多條直線

方法源碼中有這樣一個(gè)@Size(multiple=4)注解,要求floats中每4個(gè)值看做一組,每組來確定一條直線的兩個(gè)端點(diǎn),不足4個(gè)的部分是無效的

兩個(gè)參數(shù)的方法內(nèi)部還是調(diào)用了四個(gè)參數(shù)的方法,在源碼中

public void drawLines(@Size(multiple=4) @NonNull float[] pts, @NonNull Paint paint) {
    drawLines(pts, 0, pts.length, paint);
}

drawLines(float[] pts, int offset, int count,Paint paint)中,

  • offset 表示跳過floats中幾個(gè)值
  • count 表示跳過offset后,數(shù)組的長度

簡單修改代碼

canvas.drawLines(floats,4,floats.length-4,mPaint);

就只會畫出(400f,200f),(600f,200f)確定的那條水平的短線


1.3 畫Potion方法

同1.2畫Line方法一樣,畫點(diǎn)的方法也是有兩種,畫一個(gè)點(diǎn)和畫多個(gè)點(diǎn)

畫一個(gè)點(diǎn)很簡單,直接看畫多個(gè)點(diǎn)

drawPoints(@Size(multiple=2) float[] pts, int offset, int count,@NonNull Paint paint) 

根據(jù)方法內(nèi)的注解得知,pts的大小要大于2,并且每兩個(gè)一組,多余的無效

簡單使用:

private void init() {
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setColor(Color.parseColor("#FF4081"));
    mPaint.setStrokeWidth(10f);
    floats = new float[]{100f,100f,300f,200f,200f};
}

protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawPoints(floats,mPaint);
}
繪制兩個(gè)點(diǎn)

floats中,有5個(gè)值,但實(shí)際有效的也就是前4個(gè)


1.3 drawPath 方法

Android 自定義View學(xué)習(xí)(三)——Paint繪制文字屬性中學(xué)習(xí)了解了setPathEffect(PathEffect effect)方法在繪制路徑時(shí)的效果,drawPath()方法往往都會配合PahtEffect來使用

 drawPath(@NonNull Path path, @NonNull Paint paint)

方法只有一個(gè),簡單使用

private void init() {
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setColor(Color.parseColor("#FF4081"));
    mPaint.setStyle(Paint.Style.STROKE);//設(shè)置風(fēng)格為空心
    mPaint.setStrokeWidth(10f);

    path = new Path();
    path.moveTo(540f,50f);
    path.lineTo(740f,300f);
    path.lineTo(340f,300f);
    path.close();//形成閉合 將(340,300)和(540,50)連接起來
}
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawPath(path, mPaint);
}
繪制等腰三角形

繪制出一個(gè)等腰三角形

主要就是用到Path這個(gè)類,這個(gè)類有很多方法除了可以繪制直線,還可以繪制曲線


1.3.1 Path類

The Path class encapsulates compound (multiple contour) geometric paths consisting of straight line segments, quadratic curves, and cubic curves.
It can be drawn with canvas.drawPath(path, paint), either filled or stroked (based on the paint's Style), or it can be used for clipping or to draw text on a path.

這個(gè)類封裝了一些可以借助一元直線方程,二遠(yuǎn)方程曲線,立方曲線的方法來繪制一些較為復(fù)雜的組合形式的集合圖形,繪制出來的風(fēng)格則是根據(jù)畫筆設(shè)置的style來決定,也可以剪切或者繪制一段文字在路徑上

除了已經(jīng)用到的moveTo()lineTo()方法,Path中還有很多add開頭的方法。有兩個(gè)重點(diǎn)方法是quadTo()cubicTo()方法


1.3.1 quadTo和cubicTo方法

quadTo()可以用來繪制二階貝塞爾曲線,也就是3個(gè)點(diǎn)確定一個(gè)曲線

private void init() {
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setColor(Color.parseColor("#FF4081"));
    mPaint.setStyle(Paint.Style.STROKE);//設(shè)置風(fēng)格為空心
    mPaint.setStrokeWidth(10f);

    path = new Path();
    path.moveTo(100f,100f);

    floats = new float[]{200f,200f,900f,100f};
    path.quadTo(200f,200f,900f,100f);
}

protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawPath(path, mPaint);
    canvas.drawPoints(floats,mPaint);
}
二階貝塞爾曲線

圖中的曲線,是由(100,100),(900,100),(200,200)三個(gè)點(diǎn)來確定。


cubicTo方法使用和quadTo用法類似,只是多了一組參數(shù),多了一個(gè)點(diǎn)而已

 path.cubicTo(200f,200f,400f,100f,900f,200f);

測試效果和quadTo很容易就分區(qū)

依稀記得高三的數(shù)學(xué)考試最后一道大題往往就是要求繪制會一個(gè)點(diǎn)的運(yùn)動軌跡,大部分都是一段曲線或者一個(gè)橢圓之類的,和這里有些類似。

這兩方法重要的是理解其中的原理,貝爾塞爾曲線等到Matrix學(xué)習(xí)結(jié)束后再進(jìn)行學(xué)習(xí)


1.3.2 addTo方法

先來看簡單用法

private void init() {
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setColor(Color.parseColor("#FF4081"));
    mPaint.setStyle(Paint.Style.STROKE);//設(shè)置風(fēng)格為空心
    mPaint.setStrokeWidth(10f);

    path = new Path();
    path.moveTo(100f,100f);
    rectF = new RectF(100, 100, 400, 400);
    path.arcTo(rectF,0,90);
}
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawPath(path, mPaint);
}
addTo方法

這個(gè)方法就是畫出一段弧線后,將弧線的起始點(diǎn)和moveTo確定的path的起始點(diǎn)進(jìn)行連接起來?;【€截取圓的一部分。圓的直徑為300,經(jīng)過很簡單分析,圓心在(250,250)的點(diǎn)


1.3.3 rLineTo方法

r就是relative,相對的縮寫

簡單使用

private void init() {
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setColor(Color.parseColor("#FF4081"));
    mPaint.setStyle(Paint.Style.STROKE);//設(shè)置風(fēng)格為空心
    mPaint.setStrokeWidth(10f);

    path = new Path();
    path.moveTo(100f,100f);
    path.rLineTo(300,200f);
 }
 
 protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawPath(path, mPaint);
    canvas.drawPoint(300,200,mPaint);
}
rLineTo方法

rLineTo方法確定的坐標(biāo)是相對于moveTo來說的,實(shí)際最終的畫出的直線的結(jié)束點(diǎn)的坐標(biāo)為(400,300)


1.3.4 addArc方法

簡單使用

private void init() {
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setColor(Color.parseColor("#FF4081"));
    mPaint.setStyle(Paint.Style.STROKE);//設(shè)置風(fēng)格為空心
    mPaint.setStrokeWidth(10f);

    path = new Path();
    path.moveTo(100f,100f);
    path.lineTo(300,200f);
    //添加弧形
    rectF = new RectF(100, 100, 400, 400);
    path.addArc(rectF,0,90);
}

protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawPath(path, mPaint);
        canvas.drawPoint(300,200,mPaint);
}
addArc方法

addArc方法和addTo方法卻別是,addArc是添加一段弧形,并不將繪制的圖形連接起來


1.4 canvas.drawTextOnPath 方法

在繪制的路徑上,繪制文字

private void init() {
    //路徑畫筆
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setColor(Color.parseColor("#FF4081"));
    mPaint.setStyle(Paint.Style.STROKE);//設(shè)置風(fēng)格為空心
    mPaint.setStrokeWidth(10f);
    //文字畫筆
    textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    textPaint.setTextSize(60f);

    //路徑
    path = new Path();
    rectF = new RectF(100, 100, 300, 400);
    path.addOval(rectF, Path.Direction.CW);

    chars = new char[]{'a','b','c','d','e'};
}

protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //繪制路徑
    canvas.drawPath(path, mPaint);
    //繪制路徑上的文字
    canvas.drawTextOnPath(chars,0,chars.length,path,0f,chars.length,textPaint);
}
在路徑上繪制文字

addOval方法中,可以改變路徑閉合的方向,簡單修改代碼

path.addOval(rectF, Path.Direction.CCW);
Path.Direction.CCW

文字在路徑內(nèi)側(cè)繪制,并且逆時(shí)針


draw系列方法中,有一個(gè)看起來很有意思的方法,drawTextRun方法,但這個(gè)方法最低要求的23

canvas.drawTextRun(chars,0,chars.length,0,chars.length,100f,100f,false,textPaint);

沒有23的真機(jī),就用了虛擬機(jī),看方法名字,以為會按照一定的方法,文字進(jìn)行滾動,可并沒有,設(shè)置為falseabcde,設(shè)置為trueedcba,不清楚在真機(jī)上啥效果

加上在開始了解Canvas中的方法,draw大致就學(xué)習(xí)到這里


2.Clip方法學(xué)習(xí)

Clip開頭的方法主要有兩個(gè):

  • clipPath() 利用Path的方法,可以裁切出一塊不規(guī)則區(qū)域畫布
  • clipRect() 可以裁切出一塊矩形畫布

還有一個(gè)已經(jīng)廢棄的clipRegion(),廢棄就不學(xué)了,直接學(xué)替代的方法


2.1 clipRect裁切規(guī)則區(qū)域畫布

簡單使用:

private void init() {
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setColor(Color.parseColor("#FF4081"));
    mPaint.setStyle(Paint.Style.FILL);
    //矩形
    rectF = new RectF(0, 0, 400, 400);
}

protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //繪制底色 黃
    canvas.drawColor(Color.YELLOW);
    //截取畫布
    canvas.clipRect(rectF);
    //截取后的畫布底色
    canvas.drawColor(Color.CYAN);
    //驗(yàn)證有效區(qū)域
    canvas.drawRect(300,300,600,600,mPaint);
}
drawRect方法

clipRect(rectF),就是在在畫布裁出以(0,0),(0,400),(400,0),(400,400)四個(gè)點(diǎn)確定的矩形。之后Canvas有效的區(qū)域便就是裁出的矩形區(qū)域,再次進(jìn)行繪制時(shí),超出這個(gè)區(qū)域便無法繪制,但裁切并不會影響c裁切前已經(jīng)繪制好的區(qū)域,clip裁切針對的是Canvas

cliprRect有這樣一個(gè)cliprRect((@NonNull RectF rect, @NonNull Region.Op op)重載方法


2.1.1 Region.Op

OpRegion類中的一個(gè)枚舉,有6個(gè)值

直接用代碼演示效果

private void init() {
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setColor(Color.WHITE);
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeWidth(5f);
    //裁切區(qū)域1
    rectF1 = new RectF(100, 100, 300, 300);
    //裁切區(qū)域2
    rectF2 = new RectF(200, 200, 400, 400);
}

 protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //繪制底色 藍(lán)
    canvas.drawColor(Color.BLUE);
    canvas.save();
    //截取畫布1
    canvas.clipRect(rectF1);
    //截取畫布2
    canvas.clipRect(rectF2,Region.Op.DIFFERENCE);
    //截取后的有效區(qū)域畫布底色
    canvas.drawColor(Color.RED);
    canvas.restore();
    //繪制輔助區(qū)域
    canvas.drawRect(rectF1, mPaint);
    canvas.drawRect(rectF2, mPaint);
}

紅色區(qū)域就代表兩次裁切后的有效區(qū)域

  • Region.Op.DIFFERENCE
Region.Op.DIFFERENCE第一次的非交集

取第一次裁切的非交集部分


  • Region.Op.INTERSECT
Region.Op.INTERSECT交集

取兩次的交集


  • Region.Op.REPLACE
Region.Op.REPLACE第二次替代第一次裁切

第二次替代第一次裁切


  • Region.Op.UNION
Region.Op.UNION兩次裁切的和

兩次裁切的和


  • Region.Op.REVERSE_DIFFERENCE
Region.Op.REVERSE_DIFFERENCE取第2次的非交集

DIFFERENCE相反,取的第2次裁切的非交集區(qū)域


  • Region.Op.XOR
Region.Op.XOR兩次的非交集

異或,取兩次交集外的區(qū)域

有點(diǎn)類似PorterDuffXfermode圖像處理的效果


2.2 CilpPath 裁切不規(guī)則畫布

裁切出一個(gè)圓形區(qū)域的畫布

private void init() {
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setColor(Color.WHITE);
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeWidth(5f);

    path = new Path();
    path.addCircle(300,300,100, Path.Direction.CCW);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //繪制底色 藍(lán)
    canvas.drawColor(Color.BLUE);
    //裁切畫布
    canvas.clipPath(path);
    //繪制裁切后的區(qū)域底色
    canvas.drawColor(Color.parseColor("#FF4081"));
    //繪制輔助圓形
    canvas.drawCircle(300,300,100,mPaint);
}
裁切圓形的畫布

clip的方法基本就學(xué)到這里


3.其他方法

畫布除了裁切外,還有可以進(jìn)行旋轉(zhuǎn)

3.1 rotate 旋轉(zhuǎn)方法

private void init() {
        rectP1 = new Paint(Paint.ANTI_ALIAS_FLAG);
        rectP1.setColor(Color.BLUE);

        rectP2 = new Paint(Paint.ANTI_ALIAS_FLAG);
        rectP2.setColor(Color.parseColor("#FF4081"));

        rectF1 = new RectF(100,100,400,400);
        rectF2 = new RectF(200,200,300,300);
}

/**
 *  旋轉(zhuǎn)畫布
 */
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //旋轉(zhuǎn)30°
    canvas.rotate(30);
    canvas.drawRect(rectF1,rectP1);
    canvas.drawRect(rectF2,rectP2);
}
旋轉(zhuǎn)30度

整個(gè)畫布進(jìn)行了順時(shí)針旋轉(zhuǎn)30°,參數(shù)為正時(shí),是順時(shí)針旋轉(zhuǎn),負(fù)數(shù)為逆時(shí)針旋轉(zhuǎn)

如果只想讓小的紅色的矩形進(jìn)行旋轉(zhuǎn),而藍(lán)色的大矩形不旋轉(zhuǎn),需要了解畫布中的


3.2 save和restore方法

簡單修改代碼,加入saverestore兩個(gè)方法

private void init() {
    rectP1 = new Paint(Paint.ANTI_ALIAS_FLAG);
    rectP1.setColor(Color.BLUE);

    rectP2 = new Paint(Paint.ANTI_ALIAS_FLAG);
    rectP2.setColor(Color.parseColor("#FF4081"));

    rectF1 = new RectF(100,100,400,400);
    rectF2 = new RectF(200,200,300,300);

}

/**
 *  旋轉(zhuǎn)畫布
 */
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawRect(rectF1,rectP1);
    canvas.save();
    //旋轉(zhuǎn)30°
    canvas.rotate(30);
    canvas.drawRect(rectF2,rectP2);
    canvas.restore();
}
旋轉(zhuǎn)小矩形

save()就是保存當(dāng)前圖層
restore() 就是把圖層恢復(fù)到最近一次save()方法前的狀態(tài)

關(guān)于保存圖層,還有一個(gè)更加強(qiáng)大的saveLayer(),這個(gè)方法就等用到時(shí),再進(jìn)行學(xué)習(xí)


3.3 translate 平移畫布

這個(gè)方法使用很簡單

@Override
protected void onDraw(Canvas canvas) {    
    super.onDraw(canvas);   
    //平移 
    canvas.translate(800,200);  
    //繪制矩形  
    canvas.drawRect(rectF1,rectP1);   
    canvas.drawRect(rectF2,rectP2); 
    //在(100,100)處繪制一個(gè)小圓,用來輔助觀察坐標(biāo)系的改變
    canvas.drawCircle(100,100,30,rectP2);
}
平移

這個(gè)方法需要注意的是,Canvas的坐標(biāo)系就進(jìn)行了改變,觀察小圓的位置


3.4 scale 縮放方法

縮放的使用也非常簡單

protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.scale(0.5F, 1.0F);
    canvas.drawBitmap(bitmap,0,0,null);
}
縮放

縮放有效值為0~1f,1表示不進(jìn)行縮放,原始大小

縮放方法有個(gè)重載方法scale(float sx, float sy, float px, float py)

簡單修改代碼

canvas.scale(0.5F, 1.0F,540,0);
指定縮放中心

px,py確定縮放中心,canvas.scale(0.5F, 1.0F)默認(rèn)為(0,0)為縮放中心,指定(540,0)為縮放中心時(shí),屏幕寬度為1080Canvas就在水平居中縮放


3.5 skew 錯(cuò)切

簡單使用

 protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.skew(0.5f,0);
    canvas.drawBitmap(bitmap,0,0,null);
}
錯(cuò)切

這幾個(gè)方法都只是簡單的調(diào)用,看了看效果,以后用到就再深入了解學(xué)習(xí)


4. 最后

Canvas的基本知識也就學(xué)習(xí)這些,遺漏的遇到再學(xué)習(xí)

篇幅有點(diǎn)長,但并不難理解,基本都是調(diào)用一下就可以比較直觀看出效果的方法

下篇學(xué)習(xí)Matrix

月餅節(jié)到了,中秋快樂 : )

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,582評論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,540評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,801評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,223評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,442評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,976評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,800評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,996評論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,233評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,702評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內(nèi)容