自定義控件三部曲之繪圖篇(十六)——給控件添加陰影效果與發光效果

前言:要麼出擊,要麼出局,命運女神總會眷顧拼勁全力的一方



相關文章:

《Android自定義控件三部曲文章索引》:http://blog.csdn.net/harvic880925/article/details/50995268


這節我們將學到如下內容:

  • 傳統地給按鈕添加陰影的方法
  • 如何給已有控件添加陰影
  • 如何給圖片添加陰影

一、layerlist給按鈕添加陰影效果

給控件添加陰影有很多方法,但平常我們給按鈕添加陰影最常用的就是使用layerlist多層繪圖來添加陰影效果,我們先來看一下給按鈕添加的陰影效果圖:


從效果圖中可以明顯看出,按鈕的外圍多了一圈灰色的陰影效果。
在開始做陰影效果之前,我們先講解一下有關layerlist的知識。
在xml中,我們有常用的幾個標籤:shape、selector、layerlist;

  • shape標籤:以前我們講過,就是利用代碼繪製出背景效果,可以定義填充色、描邊、圓角、漸變等。不瞭解的同學可以參考下:《詳解shape標籤》 
  • selector標籤:用於定義在用戶不同的動作狀態下,使用不同的背景值。有關selector的知識,博主沒有講過,也不打算再講了,難度不大,自己搜幾個帖子就能學會了。
  • layerlist標籤:這個標籤的主要作用就是將多個圖層按照順序疊起來,做爲一個背景圖來顯示。

1、layerlist示例:

layerlist標籤就是模擬Photoshop中圖層的概念,把每一張圖層按照順序疊加起來,做爲背景圖來顯示;
我們先來看一下簡單的例子,我們要顯示一下兩隻蝸牛的圖片:


它由三張圖片組成:
一張純藍色的背景:(blog1_1.png)


一隻黃蝸牛:(blog1_2.png)


一隻土色蝸牛:(blog1_3.png)


我們先定義一個layerlist的文件(shade.xml)

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/blog1_1"/>
    <item android:drawable="@drawable/blog1_2"/>
    <item android:drawable="@drawable/blog1_3"/>
</layer-list>
這裏分別將上面的三張圖片做爲item添加給layer-list;效果圖就是一開始演示的那樣。layer-list使用起來很簡單,只需要把每一層設置爲其中的item即可。
有一點需要注意,layer-list標籤的Item中不僅可以設置drawable,也可以設置shape、selector,我們下面一一做下嘗試:

2、layer-list與shape標籤

編寫控件陰影drawable代碼
上面我們使用使用的是layer-list中item的drawable屬性來直接引入圖片,其實除了drawable屬性,item還有另外幾個屬性:
  • android:top 頂部的偏移量
  • android:bottom 底部的偏移量
  • android:left 左邊的偏移量
  • android:right 右邊的偏移量
這四個偏移量和控件的margin設置差不多,都是外間距的效果。如果不設置偏移量,前面的圖層就完全擋住了後面的圖層,從而也看不到後面的圖層效果了。
言歸正轉,先來看看如何在layer-list中使用shape標籤:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape>
            <corners android:radius="25dp"/>
            <solid android:color="#E4E4E4"/>
        </shape>
    </item>
    <item android:left="2dp" android:top="2dp"
          android:bottom="2dp" android:right="2dp">
        <shape>
            <corners android:radius="25dp"/>
            <solid android:color="#FFFFFF"/>
        </shape>
    </item>
</layer-list>
上面的代碼實現的效果是這樣的:

大家看到類似陰影的效果了吧,不錯,這段代碼就是實現按鈕陰影的代碼,我們來仔細看一下
首先,它使用layer-list將兩層shape疊加在一起,底部的shape代碼爲:

<item>
    <shape>
        <corners android:radius="25dp"/>
        <solid android:color="#E4E4E4"/>
    </shape>
</item>
底部是一個灰色的矩形,它的四個角被圓角化,並且填充爲灰色。
上層繪製的shape對應的代碼爲:

<item android:left="2dp" android:top="2dp"
      android:bottom="2dp" android:right="2dp">
    <shape>
        <corners android:radius="25dp"/>
        <solid android:color="#FFFFFF"/>
    </shape>
</item>
它同樣繪製的是一個四個角都被圓角化的矩形,但填充顏色是純白色。爲了露出底層的灰色陰影,我們需要給上層的shape加上邊距,這也就是item的 android:left=”2dp” android:top=”2dp” android:bottom=”2dp” android:right=”2dp”這四個屬性的作用,相當於margin的作用。
使用陰影drawable
在寫好layer-list以後,我們需要在按鈕控件中使用它:

<Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:layout_margin="10dp"
        android:background="@drawable/layer_shape_list"
        android:text="帶陰影的按鈕"
        android:textColor="#ff0000"/>
我們來看下效果:

從效果圖中可以看到,我們雖然實現了帶陰影的按鈕效果,但是在點擊時卻沒有任何狀態變化,這對於按鈕是完全不能接受的,所以我們需要給按鈕添加上狀態變化,這就需要用到selector標籤了

3、layer-list與selector標籤

改造方法一:使用layer-list做根結點
下面我們對上面shape的代碼進行改造,當用戶手指按下的時候,將前景色改爲黃色,代碼爲

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape>
            <corners android:radius="25dp"/>
            <solid android:color="#E4E4E4"/>
        </shape>
    </item>
    <item android:left="2dp" android:top="2dp"
          android:bottom="2dp" android:right="2dp">
        <selector>
            <item android:state_pressed="true">
                <shape>
                    <corners android:radius="25dp"/>
                    <solid android:color="#FFFF00"/>
                </shape>
            </item>
            <item>
                <shape>
                    <corners android:radius="25dp"/>
                    <solid android:color="#FFFFFF"/>
                </shape>
            </item>
        </selector>
    </item>
</layer-list>
我們先來看一下效果,然後再來看代碼

這裏明顯實現了當用戶點擊時前景變化的功能。下面我們再來講解下代碼
首先,這裏同樣是繪製兩層layer,第一層,依然是陰影層,代碼沒動:

<item>
    <shape>
        <corners android:radius="25dp"/>
        <solid android:color="#E4E4E4"/>
    </shape>
</item>
在第一層繪製完成以後,當繪製第二層時就出現問題了:
<item android:left="2dp" android:top="2dp"
      android:bottom="2dp" android:right="2dp">
    <selector>
        <item android:state_pressed="true">
            <shape>
                <corners android:radius="25dp"/>
                <solid android:color="#FFFF00"/>
            </shape>
        </item>
        <item>
            <shape>
                <corners android:radius="25dp"/>
                <solid android:color="#FFFFFF"/>
            </shape>
        </item>
    </selector>
</item>
第二層中,會對當前用戶狀態做判斷,如果用戶當前是按下狀態,則繪製:
<item android:state_pressed="true">
    <shape>
        <corners android:radius="25dp"/>
        <solid android:color="#FFFF00"/>
    </shape>
</item>
如果是其它狀態,則繪製默認圖像:
<item>
    <shape>
        <corners android:radius="25dp"/>
        <solid android:color="#FFFFFF"/>
    </shape>
</item>
所以對於layer-list標籤,從這裏也可以看出來:它的繪製是逐層繪製的,層與層之間是沒有任何影響的,每一層可以單獨設置selector標籤來響應不同的用戶操作狀態。
改造方法二:使用selector做根結點
上面我們使用layer-list來做根結點來繪製出按鈕的不同狀態響應的效果,對selector、layer-list使用熟悉的同學,應該還可以想到另一種實現方式,使用selector做爲根結點來實現響應不同用戶操作。
我們先直接上代碼吧:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <layer-list>
            <item>
                <shape>
                    <corners android:radius="25dp"/>
                    <solid android:color="#E4E4E4"/>
                </shape>
            </item>
            <item android:left="2dp" android:top="2dp"
                  android:bottom="2dp" android:right="2dp">
                <shape>
                    <corners android:radius="25dp"/>
                    <solid android:color="#FFFF00"/>
                </shape>
            </item>
        </layer-list>
    </item>

    <item>
        <layer-list>
            <item>
                <shape>
                    <corners android:radius="25dp"/>
                    <solid android:color="#E4E4E4"/>
                </shape>
            </item>
            <item android:left="2dp" android:top="2dp"
                  android:bottom="2dp" android:right="2dp">
                <shape>
                    <corners android:radius="25dp"/>
                    <solid android:color="#FFFFFF"/>
                </shape>
            </item>
        </layer-list>
    </item>

</selector>
同樣我們先來看一下使用代碼與效果,然後再來講解實現原理:
使用方法,同樣是做爲background引入:
<Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:layout_margin="10dp"
        android:background="@drawable/selector_layer_list"
        android:text="Selector爲根的按鈕"
        android:textColor="#ff0000"/>
效果圖如下:

很明顯,實現了與上面layer-list標籤爲根同樣的效果,我們現在來看一下代碼原理:
代碼看起來很長,很唬人,其實原理很簡單,它就是根據當前不同的狀態,繪製不同的圖形,當用戶是按壓狀態時,通過layer-list繪製出一下最上層是黃色,底層是灰色的按鈕背景圖像:

<item android:state_pressed="true">
    <layer-list>
        <item>
            <shape>
                <corners android:radius="25dp"/>
                <solid android:color="#E4E4E4"/>
            </shape>
        </item>
        <item android:left="2dp" android:top="2dp"
              android:bottom="2dp" android:right="2dp">
            <shape>
                <corners android:radius="25dp"/>
                <solid android:color="#FFFF00"/>
            </shape>
        </item>
    </layer-list>
</item>
然後在其它狀態時,繪製一個前景色是白色,背景色是灰色的按鈕背景圖:
<item>
    <layer-list>
        <item>
            <shape>
                <corners android:radius="25dp"/>
                <solid android:color="#E4E4E4"/>
            </shape>
        </item>
        <item android:left="2dp" android:top="2dp"
              android:bottom="2dp" android:right="2dp">
            <shape>
                <corners android:radius="25dp"/>
                <solid android:color="#FFFFFF"/>
            </shape>
        </item>
    </layer-list>
</item>
這部分代碼難度不大,就不再講了。

4、存在問題

由於使用layer-list標籤實現的陰影只能做爲background引入,所以如果對你是文字時,它的陰影效果就變成了這樣:
<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="文字的陰影效果"
        android:layout_margin="10dp"
        android:padding="10dp"
        android:background="@drawable/layer_shape_list"/>
對應效果圖爲:

看起來跟按鈕一個樣 - _ -!!! 很囧有沒有,文字的陰影應該是這樣的纔對:


所以我們下面就要開始講解如何實現文字的陰影效果啦,嘿嘿

二、Paint.setShadowLayer實現陰影效果

上面我們講了利用layer-list只能實現按鈕的陰影效果,對於文字和圖片都無法實現陰影效果,除了layer-list,我們只能用自定義控件來實現陰影效果了,Paint中有一個專門用來實現陰影效果的函數setShadowLayer,我們先來看看這個函數實現的陰影效果圖:


從效果圖中可以看出setShadowLayer函數能夠實現:

  • 定製陰影模糊程度
  • 定製陰影偏移距離
  • 清除陰影和顯示陰影

1、setShadowLayer構造函數

看起來setShadowLayer好像能夠完成陰影定製的方方面面,我們先來看看它的構造函數:

public void setShadowLayer(float radius, float dx, float dy, int color)
它參數的意義如下:
  • float radius:意思是模糊半徑,radius越大越模糊,越小越清晰,但是如果radius設置爲0,則陰影消失不見;有關清除陰影的問題,下面我們會專門講。
  • float dx:陰影的橫向偏移距離,正值向右偏移,負值向左偏移
  • float dy:陰影的縱向偏移距離,正值向下偏移,負值向上偏移
  • int color:繪製陰影的畫筆顏色,即陰影的顏色(對圖片陰影無效)
我們這裏需要着重講兩個點:一個是模糊半徑,另一個是繪製陰影的畫筆顏色爲什麼對圖片無效:
模糊半徑的具體意義:
setShadowLayer使用的是高斯模糊算法,高斯模糊的具體算法是:對於正在處理的每一個像素,取周圍若干個像素的RGB值並且平均,然後這個平均值就是模糊處理過的像素,如果對圖片中的所有像素都這麼處理的話,處理完成的圖片就會變得模糊。
取周圍像素的半徑就是模糊半徑.很容易知道,模糊半徑越大,所得平均像素與原始像素相差就越大,也就越模糊
繪製陰影的畫筆顏色爲什麼對圖片無效
從上面的效果圖中可以看出,使用setShadowLayer所產生的陰影,對於文字和繪製的圖形的陰影都是使用自定義的陰影畫筆顏色來畫的,而圖片的陰影則是直接產生一張相同的圖片,僅對陰影圖片的邊緣進行模糊。
大家可能會疑問,會什麼對圖片的處理是生成一張相同的背景圖片呢?這是因爲爲了給圖片添加陰影,如果統一使用某一種顏色來做陰影可能會與圖片的顏色相差很大,而且不協調,比如某張圖片的色彩非常豐富,而陰影如果使用灰色來做,可能就會顯得很突兀,所以爲了解決這個問題,針對圖片的陰影就不再是統一顏色了,而是複製出這張圖片,把複製出的圖片的邊緣進行模糊,做爲陰影;但這樣又會引起一個問題,就是如果我們想把圖片的陰影做成灰色怎麼辦?使用setShadowLayer自動生成陰影是沒辦法了,在下篇我們會具體來講,如何給圖片添加指定顏色的陰影。

注意:這裏有一點需要非常注意的是setShadowLayer只有文字繪製陰影支持硬件加速,其它都不支持硬件加速,所以爲了方便起見,我們需要在自定義控件中禁用硬件加速。

2、示例一:初步使用setShadowLayer

多說無益,還是直接來個例子比較實在,本次示例效果圖如下:

這裏實現了對文本,圖形,Image的陰影效果;具體的代碼如下:

public class ShadowLayerView extends View {
    private Paint mPaint = new Paint();
    private Bitmap mDogBmp;
    public ShadowLayerView(Context context) {
        super(context);
        init();
    }

    public ShadowLayerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ShadowLayerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init(){
        setLayerType( LAYER_TYPE_SOFTWARE , null);
        mPaint.setColor(Color.GREEN);
        mPaint.setTextSize(25);
        mPaint.setShadowLayer(1, 10, 10, Color.GRAY);
        mDogBmp = BitmapFactory.decodeResource(getResources(),R.drawable.dog);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.drawText("啓艦大SB",100,100,mPaint);

        canvas.drawCircle(200,200,50,mPaint);

        canvas.drawBitmap(mDogBmp,null,new Rect(200,300,200+mDogBmp.getWidth(),300+mDogBmp.getHeight()),mPaint);
    }
}
代碼看起來很長,其實就是自定義了一個控件,在裏面畫了點東東;我們分別來看下吧
首先是初始化,在初始化時設置畫筆的顏色

private void init(){
    setLayerType( LAYER_TYPE_SOFTWARE , null);
    mPaint.setColor(Color.GREEN);
    mPaint.setTextSize(25);
    mPaint.setShadowLayer(1, 10, 10, Color.GRAY);
    mDogBmp = BitmapFactory.decodeResource(getResources(),R.drawable.dog);
}
在初始化的時候,就是先禁用硬件加速,然後設置paint的屬性,由於我們需要畫圖片,所以先把要畫的圖片加載進來。這裏需要注意兩個顏色:
mPaint.setColor(Color.GREEN);
mPaint.setShadowLayer(1, 10, 10, Color.GRAY);
mPaint.setColor指的是設置畫筆的顏色是綠色,從效果圖中也可以看出來畫出來的字體和圓形都是綠色的
而mPaint.setShadowLayer中設置的 Color.GRAY,指的是陰影的顏色,從效果圖中也明顯可以看出,字體和陰影的顏色都是灰色的。
然後就是onDraw的繪圖部分了,這裏就沒什麼好講的了,如果從頭看到這裏的話,canvas的操作應該很熟練了;

3、示例二:setShadowLayer各參數意義

下面我們就來實現一下這部分開篇時的效果,動態添加setShadowLayer中的各個參數,就可以明顯看出來它們的作用:
在上面的代碼上面,我們講setShadowLayer變成了動態設置,代碼如下:

public class ShadowLayerView extends View {
    private Paint mPaint = new Paint();
    private Bitmap mDogBmp;
    private int mRadius = 1,mDx = 10,mDy = 10;
    public ShadowLayerView(Context context) {
        super(context);
        init();
    }

    public ShadowLayerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ShadowLayerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init(){
        setLayerType( LAYER_TYPE_SOFTWARE , null);
        mPaint.setColor(Color.GREEN);
        mPaint.setTextSize(25);
        mDogBmp = BitmapFactory.decodeResource(getResources(),R.drawable.dog);
    }


    public void changeRadius() {
        mRadius++;
        postInvalidate();
    }

    public void changeDx() {
        mDx+=5;
        postInvalidate();
    }

    public void changeDy() {
        mDy+=5;
        postInvalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        mPaint.setShadowLayer(mRadius, mDx, mDy, Color.GRAY);

        canvas.drawText("啓艦大SB",100,100,mPaint);

        canvas.drawCircle(200,200,50,mPaint);

        canvas.drawBitmap(mDogBmp,null,new Rect(200,300,200+mDogBmp.getWidth(),300+mDogBmp.getHeight()),mPaint);
    }
}
這段代碼難度並不大,只是將 mPaint.setShadowLayer中的各參數寫成了變量,並向外暴露了幾個接口changeRadius()、changeDx()、changeDy();當外部調用這些接口時,增加對應的變量,並且重繪控件;
由於每次重繪控件都肯定會調用onDraw方法,所以,我們將mPaint.setShadowLayer的設置放到onDraw方法裏來,以確保每次重繪時mPaint.setShadowLayer的設置都會被更新。
在使用時:

public class MyActivity extends Activity implements View.OnClickListener{
    private ShadowLayerView mShadowLayerView;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mShadowLayerView = (ShadowLayerView)findViewById(R.id.shadowlayerview);
        findViewById(R.id.radius_btn).setOnClickListener(this);
        findViewById(R.id.dx_btn).setOnClickListener(this);
        findViewById(R.id.dy_btn).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.radius_btn:
                mShadowLayerView.changeRadius();
                break;
            case R.id.dx_btn:
                mShadowLayerView.changeDx();;
                break;
            case R.id.dy_btn:
                mShadowLayerView.changeDy();
                break;
        }
    }
}
使用代碼很簡單,就不再講了。
效果圖如下:

從效果圖中可以明顯看到各個參數的區別,但正是通過效果圖,我們可以明顯得看出兩個結論:

  • 1、圖片的陰影是不受陰影畫筆顏色影響的,它是一張圖片的副本;
  • 2、無論是圖片還是圖形,模糊時,僅模糊邊界部分,隨着模糊半徑的增大,向內、向外延伸;其實很好理解這個問題:由於模糊半徑的增大,高斯模糊向周邊取值的範圍在增大,所以向內、向外延伸的距離就會更大

4、Paint.clearShadowLayer()清除陰影

上面我們講解了使用setShadowLayer添加陰影的問題,下面我們再來看看如何清除陰影的。清除陰影其實有兩個方法,可以將setShadowLayer的radius的值設爲0,也可以使用專門的清除陰影的函數:

//Paint系函數:清除ShadowLayer陰影
public void clearShadowLayer() 
將setShadowLayer的radius的值設爲0來清除陰影的用法,我這裏就不再演示了,大家可以自己試試,我們這裏嘗試下使用clearShadowLayer() 來清除陰影的用法。
在上面函數的基礎上,我們另外添加一個變量來控制當前是否顯示陰影:

public class ShadowLayerView extends View {
    …………
    private boolean mSetShadow = true;
    …………
    public void clearShadow(){
        mSetShadow = false;
        postInvalidate();
    }

    public void showShadow(){
        mSetShadow = true;
        postInvalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (mSetShadow) {
            mPaint.setShadowLayer(mRadius, mDx, mDy, Color.GRAY);
        }else {
            mPaint.clearShadowLayer();
        }

        canvas.drawText("啓艦大SB",100,100,mPaint);

        canvas.drawCircle(200,200,50,mPaint);

        canvas.drawBitmap(mDogBmp,null,new Rect(200,300,200+mDogBmp.getWidth(),300+mDogBmp.getHeight()),mPaint);
    }
}
修改的代碼很簡單,增加一個變量mSetShadow來控制當前是否顯示陰影,如果需要顯示陰影就調用mPaint.setShadowLayer(mRadius, mDx, mDy, Color.GRAY);設置陰影,如果不需要顯示陰影就調用mPaint.clearShadowLayer();來清除陰影;
對於使用btn調用clearShadow()、showShadow()這兩個接口的用法,就不再帖代碼了,沒啥難度,源碼裏也有;
效果圖如下:

在目前的所有例子中,我們的定義控件在xml中使用時,layout_widht、layout_height都統一設置成match_parent或者fill_parent來強制全屏;是時間教大家如何使用wrap_content屬性,如何讓控件自已計算高度了,下篇我們就來看看這個問題。

源碼在文章底部給出

三、TextView及其派生類使用ShadowLayer添加陰影效果

上面我們通過自定義控件來實現了自定義陰影效果,那麼問題來了,如果我需要給已有的控件添加陰影效果,實現下面這樣的效果:


1、XML實現

從上面可以看到,TextView,Button,EditView中的文字都具有陰影效果。那是怎麼實現的呢?難道我們需要在原生控件的甚而上派生一個類在onDraw裏使用setShadowLayer來繪製陰影嗎?
答案當然不是,setShadowLayer是API 1 就已經引入的方法,同樣,對於TextView和從TextView派生的類都自然具體XML屬性來設置陰影。這幾個設置陰影的XML屬性如下:

<TextView
       …………
        android:shadowRadius="3"
        android:shadowDx="5"
        android:shadowDy="5"
        android:shadowColor="@android:color/darker_gray"/>
這幾個屬性的意義非常容易理解,直接對應setShadowLayer的幾個參數setShadowLayer(float radius, float dx, float dy, int color),但這幾個屬性只有TextVIew及其派生類纔會有,其它類是沒有的,TextVIew的派生類如下:

所以一般我們使用的Button和EditText是可以使用Xml來實現陰影的。

2、代碼實現

既然能通過XML實現,當然也能會代碼版了,TextView及其派生類,都有一個Paint.setShadowLayer的同名方法:

//TextView中的設置陰影函數
public void setShadowLayer(float radius, float dx, float dy, int color) 
通過該方法就很容易來實現TextView及其派生類的陰影了。
使用示例如下:
TextView tv = (TextView)findViewById(R.id.tv);
tv.setShadowLayer(2,5,5, Color.GREEN);
效果與上面的一樣,這裏就不再講了,源碼裏都會有。
源碼在文章底部給出

四、SetMaskFilter之BlurMaskFilter實現發光效果

前面我們講了如何給控件添加陰影效果,其它跟陰影效果類似的還有一個發光效果:

上面就是我們這節要講的發光效果,在這個效果圖中,總共涉及了三個內容的發光效果:文字、圖形和Bitmap圖像。
從最後一個小狗的Bitmap所形成的發光效果中可以看到,與setShadowLayer一樣,發光效果也只會影響邊緣部分圖像,內部圖像是不受影響的。
從第三個圖形(紅綠各一半的Bitmap)中可以看到:發光效果是無法指定發光顏色的,採用邊緣部分的顏色取樣來進行模糊發光。所以邊緣是什麼顏色,發出的光也就是什麼顏色的。
所以初步我們對發光效果有如下結論:

  • 與setShadowLayer一樣,發光效果也是使用的高斯模糊,並且只會影響邊緣部分圖像,內部圖像是不受影響的
  • 發光效果是無法指定發光顏色的,採用邊緣部分的顏色取樣來進行模糊發光。所以邊緣是什麼顏色,發出的光也就是什麼顏色的。

1、SetMaskFilter之BlurMaskFilter概述

Paint函數的的setMaskFilter聲明如下:

public MaskFilter setMaskFilter(MaskFilter maskfilter)

前面我們講到setColorFilter來設置顏色濾鏡,與setColorFilter一樣,setMaskFilter中的MaskFilter也是沒有具體實現的,也是通過派生子類來實現具體的不同功能的,MaskFilter有兩個派生類BlurMaskFilter和EmbossMaskFilter,其中BlurMaskFilter就是我們這段要講的實現發光效果的子類,而EmbossMaskFilter是用來實現浮雕效果的,用處很少,這裏就不再講了。另一點需要注意的是,setMaskFilter是不支持硬件加速的,必須關閉硬件加速纔可以。
BlurMaskFilter的構造函數如下:

public BlurMaskFilter(float radius, Blur style)
其中:
  • float radius:用來定義模糊半徑,同樣是高斯模糊算法。
  • Blur style:發光樣式,有內發光、外發光、和內外發光,分別對應:Blur.INNER(內發光)、Blur.SOLID(外發光)、Blur.NORMAL(內外發光)、Blur.OUTER(僅發光部分可見),這幾個模式,後面我們會逐個來展示用法。
下面我們簡單舉一個例子來看看用法先,這個例子的代碼如下:
public class BlurMaskFilterView extends View {
    private Paint mPaint;
    public BlurMaskFilterView(Context context) {
        super(context);
        init();
    }

    public BlurMaskFilterView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public BlurMaskFilterView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init(){
        setLayerType(LAYER_TYPE_SOFTWARE,null);
        mPaint = new Paint();
        mPaint.setColor(Color.RED);
        mPaint.setMaskFilter(new BlurMaskFilter(50, Blur.INNER));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.drawCircle(200,200,100,mPaint);
    }
}
這裏使用起來非常容易,只需要在paint的時候調用setMaskFilter將BlurMaskFilter的實例設置進行就可以了。這裏使用的內發光模式。我們來看下效果圖:

很明顯的內發光效果。下面我們分別來看看各種模式下的發光效果。

2、BlurStyle發光效果

(1)、Blur.INNER——內發光


(2)、Blur.SOLID——外發光


(3)、Blur.NORMAL——內外發光


(4)、Blur.OUTER——僅顯示發光效果


Blur.OUTER比較特殊,這種模式下僅會顯示發光效果,會把原圖像中除了發光部分,全部變爲透明!
大家是否可以看出來發光效果與setShadowLayer所生成的陰影之間有什麼聯繫?
setShadowLayer所生成的陰影,其實就是將新建的陰影圖形副本進行發光效果並且位移一定的距離而已。下篇我們就會利用這個原理來生成圖片指定顏色的陰影效果。

到這裏,這篇文章就結束了,下篇將繼續給大家講解如何給圖片添加指定顏色的陰影效果,並且初步教大家如何將其封裝成一個控件。


如果本文有幫到你,記得加關注哦

源碼下載地址:http://download.csdn.net/detail/harvic880925/9566615

請大家尊重原創者版權,轉載請標明出處:http://blog.csdn.net/harvic880925/article/details/51818489 謝謝

如果你喜歡我的文章,那麼你將會更喜歡我的微信公衆號,將定期推送博主最新文章與收集乾貨分享給大家(一週一次)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章