前言:要么出击,要么出局,命运女神总会眷顾拼劲全力的一方
相关文章:
《Android自定义控件三部曲文章索引》:http://blog.csdn.net/harvic880925/article/details/50995268
这节我们将学到如下内容:
从效果图中可以明显看出,按钮的外围多了一圈灰色的阴影效果。
在开始做阴影效果之前,我们先讲解一下有关layerlist的知识。
在xml中,我们有常用的几个标签:shape、selector、layerlist;
它由三张图片组成:
一张纯蓝色的背景:(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即可。
<?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>底部是一个灰色的矩形,它的四个角被圆角化,并且填充为灰色。
<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的作用。
<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标签了
<?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标签来响应不同的用户操作状态。
<?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>同样我们先来看一下使用代码与效果,然后再来讲解实现原理:
<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>这部分代码难度不大,就不再讲了。
<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"/>对应效果图为:
看起来跟按钮一个样 - _ -!!! 很囧有没有,文字的阴影应该是这样的才对:
所以我们下面就要开始讲解如何实现文字的阴影效果啦,嘿嘿
从效果图中可以看出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; } } }使用代码很简单,就不再讲了。
从效果图中可以明显看到各个参数的区别,但正是通过效果图,我们可以明显得看出两个结论:
//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属性,如何让控件自已计算高度了,下篇我们就来看看这个问题。
<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来实现阴影的。
//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所生成的阴影,其实就是将新建的阴影图形副本进行发光效果并且位移一定的距离而已。下篇我们就会利用这个原理来生成图片指定颜色的阴影效果。
到这里,这篇文章就结束了,下篇将继续给大家讲解如何给图片添加指定颜色的阴影效果,并且初步教大家如何将其封装成一个控件。
如果本文有帮到你,记得加关注哦
源码下载地址:http://download.csdn.net/detail/harvic880925/9566615
请大家尊重原创者版权,转载请标明出处:http://blog.csdn.net/harvic880925/article/details/51818489 谢谢
如果你喜欢我的文章,那么你将会更喜欢我的微信公众号,将定期推送博主最新文章与收集干货分享给大家(一周一次)