Android自定義View進(jìn)階:繪制基本形狀(一)

Android自定義View基礎(chǔ):坐標(biāo)系
Android自定義View基礎(chǔ):角度弧度
Android自定義View進(jìn)階:分類與流程

1.Canvas的常用操作速查表

API

2.Canvas詳解

創(chuàng)建畫筆:

//創(chuàng)建畫筆
private Paint paint = new Paint();
private void initPaint(){
        mPaint.setColor(Color.BLACK);       //設(shè)置畫筆顏色
        mPaint.setStyle(Paint.Style.FILL);  //設(shè)置畫筆模式為填充
        mPaint.setStrokeWidth(10f);         //設(shè)置畫筆寬度為10px
}

//構(gòu)造函數(shù)
  public PaintView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }

繪制點(diǎn):

   canvas.drawPoint(200, 200, mPaint);     //在坐標(biāo)(200,200)位置繪制一個(gè)點(diǎn)
        canvas.drawPoints(new float[]{          //繪制一組點(diǎn),坐標(biāo)位置由float數(shù)組指定
                500,500,
                500,600,
                500,700
        },mPaint);
圖例

繪制直線:

//繪制直線需要兩個(gè)點(diǎn),初始點(diǎn)和結(jié)束點(diǎn),同樣繪制直線也可以繪制一條或者繪制一組:
    canvas.drawLine(300,300,500,600,mPaint);    // 在坐標(biāo)(300,300)(500,600)之間繪制一條直線
        canvas.drawLines(new float[]{               // 繪制一組線 每四數(shù)字(兩個(gè)點(diǎn)的坐標(biāo))確定一條線
                100,200,200,200,
                100,300,200,300
        },mPaint);

繪制矩形:
確定確定一個(gè)矩形最少需要四個(gè)數(shù)據(jù),就是對(duì)角線的兩個(gè)點(diǎn)的坐標(biāo)值,這里一般采用左上角和右下角的兩個(gè)點(diǎn)的坐標(biāo)。
關(guān)于繪制矩形,Canvas提供了三種重載方法,第一種就是提供四個(gè)數(shù)值(矩形左上角和右下角兩個(gè)點(diǎn)的坐標(biāo))來(lái)確定一個(gè)矩形進(jìn)行繪制。 其余兩種是先將矩形封裝為Rect或RectF(實(shí)際上仍然是用兩個(gè)坐標(biāo)點(diǎn)來(lái)確定的矩形),然后傳遞給Canvas繪制,如下:

    // 第一種
    canvas.drawRect(100,100,800,400,mPaint);

    // 第二種
    Rect rect = new Rect(100,100,800,400);
    canvas.drawRect(rect,mPaint);

    // 第三種
    RectF rectF = new RectF(100,100,800,400);
    canvas.drawRect(rectF,mPaint);

以上三種方法所繪制出來(lái)的結(jié)果是完全一樣的。


看到這里,相信很多觀眾會(huì)產(chǎn)生一個(gè)疑問(wèn),為什么會(huì)有Rect和RectF兩種??jī)烧哂惺裁磪^(qū)別嗎?
答案當(dāng)然是存在區(qū)別的,兩者最大的區(qū)別就是精度不同,Rect是int(整形)的,而RectF是float(單精度浮點(diǎn)型)的。除了精度不同,兩種提供的方法也稍微存在差別,在這里我們暫時(shí)無(wú)需關(guān)注,想了解更多參見官方文檔 RectRectF
繪制圓角矩形:
繪制圓角矩形也提供了兩種重載方式,如下:
// 第一種
RectF rectF = new RectF(100,100,800,400);
canvas.drawRoundRect(rectF,30,30,mPaint);

    // 第二種
    canvas.drawRoundRect(100,100,800,400,30,30,mPaint);

上面兩種方法繪制效果也是一樣的,但鑒于第二種方法在API21的時(shí)候才添加上,所以我們一般使用的都是第一種。


下面簡(jiǎn)單解析一下圓角矩形的幾個(gè)必要的參數(shù)的意思。
很明顯可以看出,第二種方法前四個(gè)參數(shù)和第一種方法的RectF作用是一樣的,都是為了確定一個(gè)矩形,最后一個(gè)參數(shù)Paint是畫筆,無(wú)需多說(shuō),與矩形相比,圓角矩形多出來(lái)了兩個(gè)參數(shù)rx 和 ry,這兩個(gè)參數(shù)是干什么的呢?
稍微分析一下,既然是圓角矩形,他的角肯定是圓弧(圓形的一部分),我們一般用什么確定一個(gè)圓形呢?
答案是圓心 和 半徑,其中圓心用于確定位置,而半徑用于確定大小
由于矩形位置已經(jīng)確定,所以其邊角位置也是確定的,那么確定位置的參數(shù)就可以省略,只需要用半徑就能描述一個(gè)圓弧了。
但是,半徑只需要一個(gè)參數(shù),但這里怎么會(huì)有兩個(gè)呢?
好吧,讓你發(fā)現(xiàn)了,這里圓角矩形的角實(shí)際上不是一個(gè)正圓的圓弧,而是橢圓的圓弧,這里的兩個(gè)參數(shù)實(shí)際上是橢圓的兩個(gè)半徑,他們看起來(lái)個(gè)如下圖:

紅線標(biāo)注的 rx 與 ry 就是兩個(gè)半徑,也就是相比繪制矩形多出來(lái)的那兩個(gè)參數(shù)。
我們了解到原理后,就可以為所欲為了,通過(guò)計(jì)算可知我們上次繪制的矩形寬度為700,高度為300,當(dāng)你讓 rx大于350(寬度的一半), ry大于150(高度的一半) 時(shí)奇跡就出現(xiàn)了, 你會(huì)發(fā)現(xiàn)圓角矩形變成了一個(gè)橢圓, 他們畫出來(lái)是這樣的 ( 為了方便確認(rèn)我更改了畫筆顏色, 同時(shí)繪制出了矩形和圓角矩形 ):

    // 矩形
    RectF rectF = new RectF(100,100,800,400);  

    // 繪制背景矩形
    mPaint.setColor(Color.GRAY);
    canvas.drawRect(rectF,mPaint);

    // 繪制圓角矩形
    mPaint.setColor(Color.BLUE);
    canvas.drawRoundRect(rectF,700,400,mPaint);


其中灰色部分是我們所選定的矩形,而里面的圓角矩形則變成了一個(gè)橢圓,實(shí)際上在rx為寬度的一半,ry為高度的一半時(shí),剛好是一個(gè)橢圓,通過(guò)上面我們分析的原理推算一下就能得到,而當(dāng)rx大于寬度的一半,ry大于高度的一半時(shí),實(shí)際上是無(wú)法計(jì)算出圓弧的,所以drawRoundRect對(duì)大于該數(shù)值的參數(shù)進(jìn)行了限制(修正),凡是大于一半的參數(shù)均按照一半來(lái)處理。
繪制橢圓:
相對(duì)于繪制圓角矩形,繪制橢圓就簡(jiǎn)單的多了,因?yàn)樗恍枰粋€(gè)矩形矩形作為參數(shù):

    // 第一種
    RectF rectF = new RectF(100,100,800,400);
    canvas.drawOval(rectF,mPaint);

    // 第二種
    canvas.drawOval(100,100,800,400,mPaint);

同樣,以上兩種方法效果完全一樣,但一般使用第一種。


繪制橢圓實(shí)際上就是繪制一個(gè)矩形的內(nèi)切圖形,原理如下,就不多說(shuō)了:

PS: 如果你傳遞進(jìn)來(lái)的是一個(gè)長(zhǎng)寬相等的矩形(即正方形),那么繪制出來(lái)的實(shí)際上就是一個(gè)圓。
繪制圓:
繪制圓形也比較簡(jiǎn)單, 如下:

canvas.drawCircle(500,500,400,mPaint);  // 繪制一個(gè)圓心坐標(biāo)在(500,500),半徑為400 的圓。

繪制圓形有四個(gè)參數(shù),前兩個(gè)是圓心坐標(biāo),第三個(gè)是半徑,最后一個(gè)是畫筆。


繪制圓?。?br> 繪制圓弧就比較神奇一點(diǎn)了,為了理解這個(gè)比較神奇的東西,我們先看一下它需要的幾個(gè)參數(shù):

// 第一種
public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint){}

// 第二種
public void drawArc(float left, float top, float right, float bottom, float startAngle,
float sweepAngle, boolean useCenter, @NonNull Paint paint) {}

從上面可以看出,相比于繪制橢圓,繪制圓弧還多了三個(gè)參數(shù):

startAngle  // 開始角度
sweepAngle  // 掃過(guò)角度
useCenter   // 是否使用中心

通過(guò)字面意思我們基本能猜測(cè)出來(lái)前兩個(gè)參數(shù)(startAngle, sweepAngel)的作用,就是確定角度的起始位置和掃過(guò)角度, 不過(guò)第三個(gè)參數(shù)是干嘛的?試一下就知道了,上代碼:

    RectF rectF = new RectF(100,100,800,400);
    // 繪制背景矩形
    mPaint.setColor(Color.GRAY);
    canvas.drawRect(rectF,mPaint);

    // 繪制圓弧
    mPaint.setColor(Color.BLUE);
    canvas.drawArc(rectF,0,90,false,mPaint);

    //-------------------------------------

    RectF rectF2 = new RectF(100,600,800,900);
    // 繪制背景矩形
    mPaint.setColor(Color.GRAY);
    canvas.drawRect(rectF2,mPaint);

    // 繪制圓弧
    mPaint.setColor(Color.BLUE);
    canvas.drawArc(rectF2,0,90,true,mPaint);

上述代碼實(shí)際上是繪制了一個(gè)起始角度為0度,掃過(guò)90度的圓弧,兩者的區(qū)別就是是否使用了中心點(diǎn),結(jié)果如下:


可以發(fā)現(xiàn)使用了中心點(diǎn)之后繪制出來(lái)類似于一個(gè)扇形,而不使用中心點(diǎn)則是圓弧起始點(diǎn)和結(jié)束點(diǎn)之間的連線加上圓弧圍成的圖形。這樣中心點(diǎn)這個(gè)參數(shù)的作用就很明顯了,不必多說(shuō)想必大家試一下就明白了。 另外可以關(guān)于角度可以參考一下這篇文章: 角度與弧度
相比于使用橢圓,我們還是使用正圓比較多的,使用正圓展示一下效果:

    RectF rectF = new RectF(100,100,800,400);
    // 繪制背景矩形
    mPaint.setColor(Color.GRAY);
    canvas.drawRect(rectF,mPaint);

    // 繪制圓弧
    mPaint.setColor(Color.BLUE);
    canvas.drawArc(rectF,0,90,false,mPaint);

    //-------------------------------------

    RectF rectF2 = new RectF(100,600,800,900);
    // 繪制背景矩形
    mPaint.setColor(Color.GRAY);
    canvas.drawRect(rectF2,mPaint);

    // 繪制圓弧
    mPaint.setColor(Color.BLUE);
    canvas.drawArc(rectF2,0,90,true,mPaint);


簡(jiǎn)要介紹Paint
看了上面這么多,相信有一部分人會(huì)產(chǎn)生一個(gè)疑問(wèn),如果我想繪制一個(gè)圓,只要邊不要里面的顏色怎么辦?
很簡(jiǎn)單,繪制的基本形狀由Canvas確定,但繪制出來(lái)的顏色,具體效果則由Paint確定
如果你注意到了的話,在一開始我們?cè)O(shè)置畫筆樣式的時(shí)候是這樣的:
mPaint.setStyle(Paint.Style.FILL); //設(shè)置畫筆模式為填充
為了展示方便,容易看出效果,之前使用的模式一直為填充模式,實(shí)際上畫筆有三種模式,如下:

STROKE                //描邊
FILL                  //填充
FILL_AND_STROKE       //描邊加填充

為了區(qū)分三者效果我們做如下實(shí)驗(yàn):

    Paint paint = new Paint();
    paint.setColor(Color.BLUE);
    paint.setStrokeWidth(40);     //為了實(shí)驗(yàn)效果明顯,特地設(shè)置描邊寬度非常大

    // 描邊
    paint.setStyle(Paint.Style.STROKE);
    canvas.drawCircle(200,200,100,paint);

    // 填充
    paint.setStyle(Paint.Style.FILL);
    canvas.drawCircle(200,500,100,paint);

    // 描邊加填充
    paint.setStyle(Paint.Style.FILL_AND_STROKE);
    canvas.drawCircle(200, 800, 100, paint);


一圖勝千言,通過(guò)以上實(shí)驗(yàn)我們可以比較明顯的看出三種模式的區(qū)別,如果只需要邊緣不需要填充內(nèi)容的話只需要設(shè)置模式為描邊(STROKE)即可。
其實(shí)關(guān)于Paint的內(nèi)容也是有不少的,這些只是冰山一角,在后續(xù)內(nèi)容中會(huì)詳細(xì)的講解Paint。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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