自定義View的繪制

這是一篇總結(jié)性地文章,內(nèi)容來自扔物線文章以及網(wǎng)易公開課,本文是自己對(duì)文章的總結(jié)
一、View的坐標(biāo)系

每一個(gè)子View都有一個(gè)自己的坐標(biāo)系,并且互不影響,坐標(biāo)系的原點(diǎn)是View的左上角,遵循右正左負(fù),下正上負(fù)

如這張圖所示,在左上定點(diǎn)為畫布的原點(diǎn),圓心的左邊是(300,300)


image.png
二、canvas的作用

1.canvas可以指定繪制的位置,以及繪制什么樣的圖層

canvas.drawColor() 在整個(gè)繪制層上繪制一層背景色

三、Paint負(fù)責(zé)繪制的風(fēng)格

繪制的顏色,抗鋸齒,繪制線條的方式,實(shí)心還是空心等等和風(fēng)格相關(guān)的都是由paint來做的

Paint.setAntiAlias(boolean aa) 設(shè)置抗鋸齒開關(guān)
Paint.setStyle(Paint.Style.STROKE) 畫出圖形的線條 不會(huì)填充整個(gè)圖形
Paint.setStrokeCap(cap) 設(shè)置線條端點(diǎn)形狀
Paint. setShader 設(shè)置著色器
Paint.setColorFilter 設(shè)置顏色的過濾效果 比如加濾鏡
Paint.setStrokeJoin 設(shè)置線條拐角的方式
Paint.setDither(boolean dither) 加入抖動(dòng)效果有規(guī)律地?cái)_亂圖像來讓圖像對(duì)于肉眼更加真實(shí)
Paint.setFilterBitmap(filterBitmap)優(yōu)化圖像放大后的效果,消除馬賽克
Paint.setPathEffect 圖像外圍輪廓線的效果
Paint.setShadowLayer 給繪制的內(nèi)容加一層陰影 BlurMaskFilter 模糊效果

1. Paint.setStrokeCap(cap)效果示意圖

畫筆圓角效果

2. Paint.setFilterBitmap(true)效果示意圖
左邊是未開啟優(yōu)化的,右邊是開啟優(yōu)化的,可以看到少了許多馬賽克的效果

設(shè)置雙線性過濾

3. Paint.setStrokeJoin效果示意圖

拐角的效果

4.使用著色器可以實(shí)現(xiàn)一種規(guī)律性的變化。著色器分為三類 線性漸變new LinearGradient,從中心進(jìn)行輻射漸變RadialGradient,像雷達(dá)
一樣掃描的漸變SweepGradient, 使用圖像來圖形上色 BitmapShader,將多個(gè)著色器組合起來使用的ComposeShader



線性的著色器示例

image.png

    /**
        * 1.線性渲染,LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[], @Nullable float positions[], @NonNull TileMode tile)
        * (x0,y0):漸變起始點(diǎn)坐標(biāo)
        * (x1,y1):漸變結(jié)束點(diǎn)坐標(biāo)
        * color0:漸變開始點(diǎn)顏色,16進(jìn)制的顏色表示,必須要帶有透明度
        * color1:漸變結(jié)束顏色
        * colors:漸變數(shù)組
        * positions:位置數(shù)組,position的取值范圍[0,1],作用是指定某個(gè)位置的顏色值,如果傳null,漸變就線性變化。
        * tile:用于指定控件區(qū)域大于指定的漸變區(qū)域時(shí),空白區(qū)域的顏色填充方法
        */
       mShader = new LinearGradient(0, 0, 500, 500, new int[]{Color.RED, Color.BLUE, Color.GREEN}, new float[]{0.f,0.7f,1}, Shader.TileMode.REPEAT);
       mPaint.setShader(mShader);
       canvas.drawRect(0,0,1000,1000, mPaint);

環(huán)形的著色器示例


image.png
   /* 環(huán)形渲染,RadialGradient(float centerX, float centerY, float radius, @ColorInt int colors[], @Nullable float stops[], TileMode tileMode)
        * centerX ,centerY:shader的中心坐標(biāo),開始漸變的坐標(biāo)
        * radius:漸變的半徑
        * centerColor,edgeColor:中心點(diǎn)漸變顏色,邊界的漸變顏色
        * colors:漸變顏色數(shù)組
        * stoops:漸變位置數(shù)組,類似掃描漸變的positions數(shù)組,取值[0,1],中心點(diǎn)為0,半徑到達(dá)位置為1.0f
        * tileMode:shader未覆蓋以外的填充模式。
        */
       mShader = new RadialGradient(250, 250, 250, new int[]{Color.GREEN, Color.YELLOW, Color.RED}, null, Shader.TileMode.CLAMP);
       mPaint.setShader(mShader);
       canvas.drawCircle(250, 250, 250, mPaint);

位圖的著色器示例


image.png
       /**
        * 位圖渲染,BitmapShader(@NonNull Bitmap bitmap, @NonNull TileMode tileX, @NonNull TileMode tileY)
        * Bitmap:構(gòu)造shader使用的bitmap
        * tileX:X軸方向的TileMode
        * tileY:Y軸方向的TileMode
              REPEAT, 繪制區(qū)域超過渲染區(qū)域的部分,重復(fù)排版
              CLAMP, 繪制區(qū)域超過渲染區(qū)域的部分,會(huì)以最后一個(gè)像素拉伸排版
              MIRROR, 繪制區(qū)域超過渲染區(qū)域的部分,鏡像翻轉(zhuǎn)排版
        */
       mShader = new BitmapShader(mBitmap, Shader.TileMode.REPEAT, Shader.TileMode.MIRROR);
       mPaint.setShader(mShader);
       canvas.drawRect(0,0,500, 500, mPaint);

組合的著色器示例

   /**
         * 組合渲染,
         * ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB, Xfermode mode)
         * ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB, PorterDuff.Mode mode)
         * shaderA,shaderB:要混合的兩種shader
         * Xfermode mode: 組合兩種shader顏色的模式
         * PorterDuff.Mode mode: 組合兩種shader顏色的模式
         */
        BitmapShader bitmapShader = new BitmapShader(mBitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
        LinearGradient linearGradient = new LinearGradient(0, 0, 1000, 1600, new int[]{Color.RED, Color.GREEN, Color.BLUE}, null, Shader.TileMode.CLAMP);
        mShader = new ComposeShader(bitmapShader, linearGradient, PorterDuff.Mode.MULTIPLY);
        mPaint.setShader(mShader);
        canvas.drawCircle(250, 250, 250, mPaint);
三、Canvas重點(diǎn)API

drawArc :畫扇形
drawPath(Path,Paint):畫自定義的圖形
drawTextOnPath:沿著一趟線繪制文字

1.對(duì)于drawPath需要傳入path對(duì)象。path對(duì)象又有幾種屬性。

addXXX 表示添加某圖形

quadTo:貝塞爾曲線
moveTo:將圓點(diǎn)移動(dòng)到指定的位置,不管畫什么圖形都是從當(dāng)前位置開始畫的,通過moveTo可以指定畫筆起始的位置。
close() 自動(dòng)封閉,假設(shè)畫了三角形的兩邊后直接調(diào)用該方法,會(huì)自動(dòng)將兩點(diǎn)連接起來形成封閉。
setFillType(Path.FillType ft) 設(shè)置填充方式

2.drawText難點(diǎn)

1.drawText指定的x,y是要繪制的文字左下角的坐標(biāo),縱坐標(biāo)y代表文字的基線


image.png

2.可以使用StaticLayout來實(shí)現(xiàn)繪制文字的自動(dòng)換行


image.png
  1. setTypeface設(shè)置字體
  2. setStrikeThruText刪除線setUnderlineText下劃線
  3. setLetterSpacing字符的間距
  4. setFontFeatureSettings使用css的方式設(shè)置字體
  5. setTextAlign對(duì)其方式
  6. setTextLocale設(shè)置繪制文字的地域文字類型。大陸簡(jiǎn)體 臺(tái)灣繁體
  7. getFontSpacing獲取推薦的行距,可以在換行繪制時(shí)使用
canvas.drawText(texts[1], 100, 150 + paint.getFontSpacing, paint);

4.canvas的裁切
繪制的內(nèi)容超出這個(gè)范圍會(huì)被裁切掉,使用clipRect方法。clipPath

5.保存canvas的一次繪制狀態(tài)
canvas.save, canvas.restore。幾何變化一定要配合save和restore來配合使用。否則將后續(xù)繪制也受之前的影響

canvas的幾何變換

canvas的幾何變換代碼執(zhí)行順序是倒序的,如果想要先旋轉(zhuǎn)再平移,那么平移代碼要寫在最上面,再寫旋轉(zhuǎn)的代碼。translate,scale,rotate,skew

6.canvas的平移
使用translate方法讓中心點(diǎn)平移到某個(gè)位置,由于canvas的幾何變換是反著的,所以如果要先平移到一個(gè)點(diǎn),再移動(dòng)回原點(diǎn)要先寫移動(dòng)回原點(diǎn)的代碼,再寫移動(dòng)到其他位置的代碼。

7.使用matris矩陣來變換
post..方法代表在前面插入,也就是變換代碼是順序執(zhí)行的
per..方法是往后邊的代碼插入,也就是和canvas的變換順序,會(huì)插入到其他變換代碼的后面。

四、使用Camera來實(shí)現(xiàn)三維旋轉(zhuǎn)

image.png

1.camera.save/restore/applytoCanvas
camera的操作是在圖像的原點(diǎn)(0,0)進(jìn)行的,因此使用camera變化是不對(duì)稱的,如果想要在圖形的中心點(diǎn)進(jìn)行變化,需要配合canvas的translate方法將canvas移動(dòng)到中心點(diǎn)進(jìn)行變化,然后再移動(dòng)回去。因?yàn)閏amera是不支持設(shè)置軸心的,軸心永遠(yuǎn)是(0,0點(diǎn))。

2.使用setLocation來將相機(jī)的位置向后移動(dòng)
相機(jī)后移投影的圖像變小。一般情況不需要移動(dòng)x,y 只需要移動(dòng)z軸。因?yàn)橄鄼C(jī)是在z軸上。
camera.setLocation(0, 0, newZ);

**3.camera的旋轉(zhuǎn)正方向

image.png

View繪制的過程

1.新繪制的總是會(huì)覆蓋之前繪制的內(nèi)容
2.View的繪制過程是從drawBackground(不能重寫),onDraw繪制主題,dispatchDraw繪制子view,drawForeground繪制前景色,而這些都是由draw方法觸發(fā)的
3.如果是直接繼承view,super.onDraw并沒有什么用,因?yàn)閛nDraw本身就是空實(shí)現(xiàn)
4.ViewGroup在通過onDraw繪制完自身后,會(huì)通過onDispatchDraw方法繪制子View
5.ViewGroup默認(rèn)會(huì)跳過draw方法而直接執(zhí)行dispatchDraw,如果想讓它繪制自身,使用setWillNotDraw(false)來執(zhí)行完整的繪制流程

hencoder的圖片

最后編輯于
?著作權(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)容