編輯:關於Android編程
這節我們將學到如下內容:
傳統地給按鈕添加陰影的方法如何給已有控件添加陰影如何給圖片添加陰影
從效果圖中可以明顯看出,按鈕的外圍多了一圈灰色的陰影效果。
在開始做陰影效果之前,我們先講解一下有關layerlist的知識。
在xml中,我們有常用的幾個標簽:shape、selector、layerlist;
它由三張圖片組成:
一張純藍色的背景:(blog1_1.png)
一只黃蝸牛:(blog1_2.png)
一只土色蝸牛:(blog1_3.png)
我們先定義一個layerlist的文件(shade.xml)
這裡分別將上面的三張圖片做為item添加給layer-list;效果圖就是一開始演示的那樣。layer-list使用起來很簡單,只需要把每一層設置為其中的item即可。
上面的代碼實現的效果是這樣的:
大家看到類似陰影的效果了吧,不錯,這段代碼就是實現按鈕陰影的代碼,我們來仔細看一下
首先,它使用layer-list將兩層shape疊加在一起,底部的shape代碼為:
底部是一個灰色的矩形,它的四個角被圓角化,並且填充為灰色。
它同樣繪制的是一個四個角都被圓角化的矩形,但填充顏色是純白色。為了露出底層的灰色陰影,我們需要給上層的shape加上邊距,這也就是item的 android:left=”2dp” android:top=”2dp” android:bottom=”2dp” android:right=”2dp”這四個屬性的作用,相當於margin的作用。
我們來看下效果:
從效果圖中可以看到,我們雖然實現了帶陰影的按鈕效果,但是在點擊時卻沒有任何狀態變化,這對於按鈕是完全不能接受的,所以我們需要給按鈕添加上狀態變化,這就需要用到selector標簽了
我們先來看一下效果,然後再來看代碼
這裡明顯實現了當用戶點擊時前景變化的功能。下面我們再來講解下代碼
首先,這裡同樣是繪制兩層layer,第一層,依然是陰影層,代碼沒動:
在第一層繪制完成以後,當繪制第二層時就出現問題了:
第二層中,會對當前用戶狀態做判斷,如果用戶當前是按下狀態,則繪制:
如果是其它狀態,則繪制默認圖像:
所以對於layer-list標簽,從這裡也可以看出來:它的繪制是逐層繪制的,層與層之間是沒有任何影響的,每一層可以單獨設置selector標簽來響應不同的用戶操作狀態。
同樣我們先來看一下使用代碼與效果,然後再來講解實現原理:
效果圖如下:
很明顯,實現了與上面layer-list標簽為根同樣的效果,我們現在來看一下代碼原理:
代碼看起來很長,很唬人,其實原理很簡單,它就是根據當前不同的狀態,繪制不同的圖形,當用戶是按壓狀態時,通過layer-list繪制出一下最上層是黃色,底層是灰色的按鈕背景圖像:
然後在其它狀態時,繪制一個前景色是白色,背景色是灰色的按鈕背景圖:
這部分代碼難度不大,就不再講了。
對應效果圖為:
看起來跟按鈕一個樣 - _ -!!! 很囧有沒有,文字的陰影應該是這樣的才對:
所以我們下面就要開始講解如何實現文字的陰影效果啦,嘿嘿
從效果圖中可以看出setShadowLayer函數能夠實現:
定制陰影模糊程度定制陰影偏移距離清除陰影和顯示陰影
public void setShadowLayer(float radius, float dx, float dy, int color)它參數的意義如下:
這裡實現了對文本,圖形,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指的是設置畫筆的顏色是綠色,從效果圖中也可以看出來畫出來的字體和圓形都是綠色的
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();當外部調用這些接口時,增加對應的變量,並且重繪控件;
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、無論是圖片還是圖形,模糊時,僅模糊邊界部分,隨著模糊半徑的增大,向內、向外延伸;其實很好理解這個問題:由於模糊半徑的增大,高斯模糊向周邊取值的范圍在增大,所以向內、向外延伸的距離就會更大
//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();來清除陰影;
在目前的所有例子中,我們的定義控件在xml中使用時,layout_widht、layout_height都統一設置成match_parent或者fill_parent來強制全屏;是時間教大家如何使用wrap_content屬性,如何讓控件自已計算高度了,下篇我們就來看看這個問題。
源碼在文章底部給出
這幾個屬性的意義非常容易理解,直接對應setShadowLayer的幾個參數setShadowLayer(float radius, float dx, float dy, int color),但這幾個屬性只有TextVIew及其派生類才會有,其它類是沒有的,TextVIew的派生類如下:
所以一般我們使用的Button和EditText是可以使用Xml來實現陰影的。
//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);效果與上面的一樣,這裡就不再講了,源碼裡都會有。
上面就是我們這節要講的發光效果,在這個效果圖中,總共涉及了三個內容的發光效果:文字、圖形和Bitmap圖像。
從最後一個小狗的Bitmap所形成的發光效果中可以看到,與setShadowLayer一樣,發光效果也只會影響邊緣部分圖像,內部圖像是不受影響的。
從第三個圖形(紅綠各一半的Bitmap)中可以看到:發光效果是無法指定發光顏色的,采用邊緣部分的顏色取樣來進行模糊發光。所以邊緣是什麼顏色,發出的光也就是什麼顏色的。
所以初步我們對發光效果有如下結論:
public MaskFilter setMaskFilter(MaskFilter maskfilter)
前面我們講到setColorFilter來設置顏色濾鏡,與setColorFilter一樣,setMaskFilter中的MaskFilter也是沒有具體實現的,也是通過派生子類來實現具體的不同功能的,MaskFilter有兩個派生類BlurMaskFilter和EmbossMaskFilter,其中BlurMaskFilter就是我們這段要講的實現發光效果的子類,而EmbossMaskFilter是用來實現浮雕效果的,用處很少,這裡就不再講了。另一點需要注意的是,setMaskFilter是不支持硬件加速的,必須關閉硬件加速才可以。
BlurMaskFilter的構造函數如下:
public BlurMaskFilter(float radius, Blur style)其中:
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)、Blur.SOLID——外發光
(3)、Blur.NORMAL——內外發光
(4)、Blur.OUTER——僅顯示發光效果
Blur.OUTER比較特殊,這種模式下僅會顯示發光效果,會把原圖像中除了發光部分,全部變為透明!
大家是否可以看出來發光效果與setShadowLayer所生成的陰影之間有什麼聯系?
setShadowLayer所生成的陰影,其實就是將新建的陰影圖形副本進行發光效果並且位移一定的距離而言。下篇我們就會利用這個原理來生成圖片指定顏色的陰影效果。
到這裡,這篇文章就結束了,下篇將繼續給大家講解如何給圖片添加指定顏色的陰影效果,並且初步教大家如何將其封裝成一個控件。
App工程文件分析關於如何創建一個最簡單的Android App請參照鏈接:《 Android學習筆記(一)環境安裝及第一個hello world 》 http://ww
Eclipse裡有很多界面組件,文件列表、編輯區、類結構等等,在這麼多界面組件裡,再打開一個Logcat就基本沒有什麼空間了。與其擠在一起還不如分開成兩個窗口。或者你
(1)布局文件activity_main.xml如下:(2)MainActivity.javapackage com.xuliugen.lockscreen;import
Eclipse是老牌的開發工具,相信早期開發android程序每一個碼農都使用過這個軟件,添加ADT插件之後就能開發android程序了。因為是開源的,所以開發起項目來還