Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android中Canvas繪圖之MaskFilter圖文詳解(附源碼下載)

Android中Canvas繪圖之MaskFilter圖文詳解(附源碼下載)

編輯:關於Android編程

如果對Canvas繪圖不熟悉,強烈建議您閱讀博文《Android中Canvas繪圖基礎詳解(附源碼下載)》,該文對Android中的Canvas繪圖基礎進行了詳細的描述。本文著重講解如何使用MaskFilter創建模糊陰影以及浮雕效果。

我們知道Canvas中的各種drawXXX方法決定了繪制的幾何圖形的形狀,而畫筆Paint則決定了以什麼效果繪制這些圖形。Paint中有一個setMaskFilter方法,該方法接收一個MaskFilter類型的參數,MaskFilter有兩個子類,分別是BlurMaskFilter和EmbossMaskFilter,可以分別用來繪制模糊陰影以及浮雕效果。如果Paint的setMaskFilter方法中傳入的是null,那麼就表示不使用任何MaskFilter。

為了演示不同MaskFilter對Canvas繪圖的影響,我做了一個App,界面如下所示:

這裡寫圖片描述

上面用Canvas的各種drawXXX方法繪制了各種圖形,下面有三個RadioButton,默認選擇的是當前繪圖沒有使用MaskFilter,第二項表示使用BlurMaskFilter,第三項表示使用EmbossMaskFilter。選炸ky"/kf/web/php/" target="_blank" class="keylink">PHP4NOmtcRSYWRpb0J1dHRvbrrzo6zPwsPmu+Gz9s/WttTTprXEtfe92rLOyv21xFVJvefD5qOstbHIu6Os0aHU8bXa0rvP7iZsZHF1bzvO3k1hc2tGaWx0ZXImcmRxdW87uvOjrMrHw7vT0MjOus61973ass7K/bXEVUm958PmtcShozwvcD4NCjxociAvPg0KPGgyIGlkPQ=="繪圖代碼">繪圖代碼

無論選擇上面的哪一項,都是用同樣的代碼來繪制圖形的,唯一不同的地方就是傳遞給畫筆Paint的setMaskFilter的參數MaskFilter不同。繪圖代碼如下所示:

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int offsetX = (int)(10 * density);
        int canvasWidth = canvas.getWidth() - offsetX;
        int canvasHeight = canvas.getHeight();
        int count = 7;
        int deltaY = canvasHeight / (count + 1);
        int smallDeltaY = deltaY / (count + 1);
        canvas.translate(offsetX, 0);

        /*---------------------------繪制文本--------------------------*/
        canvas.translate(0, smallDeltaY);
        canvas.drawText(繪制文本, 0, fontSize, paint);

        /*---------------------------繪制點--------------------------*/
        canvas.translate(0, deltaY + smallDeltaY);
        int pointDeltaX = canvasWidth / 3;
        //設置畫筆線寬
        paint.setStrokeWidth(20 * density);
        //繪制BUTT類型的點
        paint.setStrokeCap(Paint.Cap.BUTT);
        canvas.drawPoint(0, 0, paint);
        //繪制ROUND類型的點
        paint.setStrokeCap(Paint.Cap.ROUND);
        canvas.drawPoint(pointDeltaX, 0, paint);
        //繪制SQUARE類型的點
        paint.setStrokeCap(Paint.Cap.SQUARE);
        canvas.drawPoint(pointDeltaX * 2, 0, paint);

        /*---------------------------繪制直線--------------------------*/
        canvas.translate(0, deltaY + smallDeltaY);
        //設置畫筆線寬
        paint.setStrokeWidth(5 * density);
        canvas.drawLine(0, 0, canvasWidth, 0, paint);

        /*---------------------------繪制弧線--------------------------*/
        canvas.translate(0, deltaY + smallDeltaY);
        paint.setStyle(Paint.Style.STROKE);
        RectF arcRecF = new RectF();
        arcRecF.left = 0;
        arcRecF.top = 0;
        arcRecF.right = deltaY * 2;
        arcRecF.bottom = deltaY;
        canvas.drawArc(arcRecF, 225, 135, true, paint);

        /*---------------------------繪制矩形--------------------------*/
        canvas.translate(0, deltaY + smallDeltaY);
        paint.setStyle(Paint.Style.FILL);
        canvas.drawRect(0, 0, canvasWidth / 2, deltaY / 2, paint);

        /*---------------------------繪制橢圓面--------------------------*/
        canvas.translate(0, deltaY + smallDeltaY);
        RectF ovalRecF = new RectF();
        ovalRecF.left = 0;
        ovalRecF.top = 0;
        ovalRecF.right = deltaY * 2;
        ovalRecF.bottom = deltaY;
        canvas.drawOval(ovalRecF, paint);

        /*---------------------------繪制Bitmap--------------------------*/
        if(bitmap != null){
            canvas.translate(0, deltaY + smallDeltaY);
            canvas.drawBitmap(bitmap, 0, 0, paint);
        }
    }

對以上代碼進行簡單說明:

首先用Canvas的drawText方法繪制了文本,見上圖中第一行繪制的文本。

然後用Canvas的drawPoint方法繪制了點,注意,我分別用BUTT、ROUND、SQUARE作為strokeCap繪制了三個點,並且特意將線寬設置的很大,以便我們容易觀察, 見上圖中第二行繪制的三個點圖形。

然後用Canvas的drawLine方法繪制了一條線段,也讓線段具有一定的線寬,見上圖中第三行繪制的線段圖形。

然後用Canvas的drawArc方法繪制了一個扇形模樣的橢圓弧,也具有一定的線寬,見上圖中第四行繪制的弧線圖形。

然後用Canvas的drawRect方法繪制了一個矩形,見上圖中第五行繪制的圖形。

然後用Canvas的drawOval方法繪制了一個橢圓,見上圖中第六行繪制的圖形。

最後用Canvas的drawBitmap方法繪制一個Bitmap,見上圖中最後一行繪制的圖形。

繪制不同類型的圖形的目的是為了讓大家更好的觀察不同類型的圖形是如何受不同MaskFilter影響的。上圖就是在給畫筆Paint設置的MaskFilter為null的情況下繪制的效果,即我們正常繪制圖形的效果。


BlurMaskFilter

選中BlurMaskFilter時,會創建模糊陰影效果,選擇的style不同的話,效果不同,模糊半徑的大小也影響效果,其效果如下所示:
這裡寫圖片描述

BlurMaskFilter的構造函數簽名是:

BlurMaskFilter(float radius, BlurMaskFilter.Blur style)

下面對上面兩個參數進行解釋:

radius
第一個參數radius是float類型,表示是Blur Radius,即陰影的模糊半徑,如上圖所示,其值越大表示圖形繪制出來越模糊,其值越小圖形越清晰。

style
第二個參數style是枚舉BlurMaskFilter.Blur類型,陰影按細節來分主要分為內陰影和外陰影,內陰影指的是陰影從圖形輪廓向內側擴張,外陰影指的是陰影從圖形輪廓向外側擴張。從陰影角度說,圖形的繪制最多由三部分組成: 外陰影 + 圖形本身內容 + 內陰影,Blur枚舉有四種值:

NORMAL
當style為NORMAL時,會同時繪制圖形本身內容+內陰影+外陰影,即正常陰影效果。
通常情況下,當我們要想使用陰影效果時,一般選擇NORMAL作為style。當陰影模糊半徑為0時,相當於沒有使用陰影效果,隨著陰影模糊半徑變大,圖形更模糊看不清。並且你還可以發現,無論陰影模糊半徑如何變化,用BUTT、SQUARE繪制的點都不受影響,繪制的Bitmap也不受影響。可以仔細觀察上圖中選中NORMAL時圖形的陰影效果。

INNER
當style為INNER時,繪制圖形內容本身+內陰影,不繪制外陰影。
當陰影模糊半徑為0時,相當於沒有使用陰影效果,隨著陰影模糊半徑變大,可以發現圖形從輪廓線向內顏色變淺。無論陰影模糊半徑如何變化,用BUTT、SQUARE繪制的點仍然都不受影響,繪制的Bitmap也不受影響。可以仔細觀察上圖中選中INNER時圖形的陰影效果。

OUTER
當style為OUTER時,不繪制圖形內容以及內陰影,只繪制外陰影,即圖形輪廓以內完全不繪制,輪廓線以內完全是空白的。
從界面效果上看就是圖形被掏空了,只是有熒光向外擴散。當陰影模糊半徑為0時,外陰影也幾乎看不到了,此時整個圖形也基本看不到,隨著陰影模糊半徑變大,外陰影逐漸明顯。無論陰影模糊半徑如何變化,用BUTT、SQUARE繪制的點仍然都不受影響,繪制的Bitmap一直基本上不可見。可以仔細觀察上圖中選中OUTER時圖形的陰影效果。

SOLID
當style為SOLID時,只繪制外陰影和圖形內容本身,不繪制內陰影。
從界面上看,由於不繪制內陰影,所以圖形輪廓線內部不會發虛。繪制的外陰影會導致輪廓線向外發虛。當陰影模糊半徑為0時,相當於沒有陰影效果,隨著陰影模糊半徑的增大,陰影效果明顯。用BUTT、SQUARE繪制的點仍然都不受影響,繪制的Bitmap也不受影響。

小結:

不同的style會影響繪制圖形的不同部分。 無論哪種style,用BUTT和SQUARE作為strokeCap繪制的正方形的點都完全不受BlurMaskFilter的影響,不過用ROUND作為strokeCap畫出的圓形的點會受到影響。 在NORMAL、INNER、SOLID作為style時,繪制的Bitmap基本完全不受影響,在OUTER作為style時,繪制的Bitmap基本不可見。所以BlurMaskFilter對Bitmap的實際用處不大。

EmbossMaskFilter

可以用EmbossMaskFilter創建浮雕效果的MaskFilter,然後通過Paint的setMaskFilter賦值給畫筆Paint。其效果如下圖所示:
這裡寫圖片描述

EmbossMaskFilter的構造函數簽名如下所示:

public EmbossMaskFilter (float[] direction, float ambient, float specular, float blurRadius)

在講解以上參數之前,先簡單介紹一下EmbossMaskFilter的作用機理。所謂的浮雕效果其實就是模擬光照效果,靠近光的一面顯得亮一點,遠離光的一面顯得暗一點,這樣就通過顏色的亮暗營造出浮雕的3D立體效果。如果熟悉OpenGL繪圖,可能就會對Phong式光照模型比較了解,一般來說,一個相對完整的光照模型=環境光 + 漫反射 + 鏡面反射。EmbossMaskFilter為了簡化參數並且突出浮雕效果,就把漫反射給去掉了,所以EmbossMaskFilter所使用的光照模型就只有環境光和鏡面反射了。

下面對各個參數進行講解:

direction
direction是一個float類型的數組,表示光線的方向,是個向量,包含三個值,分別是x分量、y分量、z分量,這三個值的絕對大小並不重要,因為direction最後在真正被Android使用時會被歸一化成一個單位向量,即變成長度為1的向量。direction中的坐標是在所畫圖形的右手坐標系中定義的,如下圖所示:
這裡寫圖片描述
如果對OpenGL開發熟悉的話肯定知道右手坐標系。我們以畫橢圓為例,橢圓的中心就是這個三維坐標系的中心點。x正半軸從中心點水平向右,y正半軸從中心點垂直向上,由於圖形在Android屏幕內,所以x和y軸都在Android屏幕的平面內。z軸經過橢圓的中心點垂直於屏幕,即同時垂直於x軸和y軸,並且其z正半軸的方向為從屏幕穿過面向我們,即z正半軸的箭頭沖向我們。我們可以通過上面的的UI調節direction中的x、y、z的值,從坐標原點(0,0,0)到(x,y,z)的向量即為光照方向。舉個例子,當x為1,y為0,z為0時,光照向量為(1,0,0),表示光是恰好沿著從x負半軸指向x正半軸,此時,由於光照從x負半軸照向x正半軸,所以橢圓上處於x負半軸的地方離光照近,被照亮,即橢圓的左側被照亮,處於x正半軸的地方則顯得較暗,則橢圓的右側則比較暗。大家可以自己調解下參數感受一下,如下圖所示:

這裡寫圖片描述

ambient
ambient表示環境光因子,float類型,取值是0到1,值越接近於0,環境光越暗,值越接近於1,環境光越亮。

specular
specular表示鏡面反射因子,float類型,取值也是0到1。鏡面反射就是模擬像鏡子一樣的高光反射,值越接近於0,鏡面反射越強,被光照照射到的地方更容易出現很白很亮的狀態,即高光效果。

blurRadius
blurRadius表示模糊半徑,是float類型,其值越大,模糊效果越明顯。


注意

最後需要注意的是,由於在GPU硬件加速模式下,Paint的setMaskFilter不被GPU支持,所以為了能夠正常使用setMaskFilter方法,我們需要將我們要繪制的View使用軟件渲染模式,具體可參見博文《Android中GPU硬件加速控制及其在2D圖形繪制上的局限》。相關代碼如下所示:

//為了確保畫筆的setMaskFilter能供起效,我們需要對MyView禁用掉GPU硬件加速
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
    //View從API Level 11才加入setLayerType方法
    //設置myView以軟件渲染模式繪圖
    myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved