Shader又被称为着色器、渲染器,它用来实现一系列的渐变、渲染效果。Android中的Shader包括以下几种。
● BitmapShader——位图 Shader
● LinearGradient——线性 Shader
● RadialGradient——光束 Shader
● SweepGradient——梯度 Shader
● ComposeShader——混合 Shader
除了第一个Shader以为,其他的Shader都比较正常,实现了名副其实的渐变、渲染效果。而与其他的Shader所产生的渐变不同,BitmapShader产生的是一个图像,这有点像Photoshop中的图像填充渐变。它的作用就是通过Paint对画布进行制定Bitmap的填充,填充时有以下几种模式可以选择。
● CLAMP拉伸——拉伸的是图片最后的一和像素,不断重复
● REPEAT重复——横向、纵向不断重复
● MIRROR镜像——横向不断翻转重复,纵向不断翻转重复
这几种模式的含义都非常好理解,与字面意思基本相同。这里最常使用的就是CLAMP拉伸模式,虽然它会拉伸最后一个像素,但是只要将图像设置为一定大小,就可以避免这种拉伸。下面来看一个例子,将一个矩形图片变成一个圆形图片。当然,通过绘制不同的图形,你也可以绘制出不同形状的图形,显示效果如图(1)所示。
图(1) 圆形图片
程序非常简单,无非就是使用BitmapShader来进行图形填充,代码如下所示。
mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.asdf);
mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint = new Paint();
mPaint.setShader(mBitmapShader);
canvas.drawCircle(500,250,200,mPaint);
在以上代码中,用一张图片创建了一支具有图像填充功能的画笔,并使用这个画笔绘制了一个圆形。这样,我们就可以看见一个圆形的图形。
这里大家可能还不能很好体会BitmapShader,这是因为选择的Bitmap的大小比较大,所以在设置BitmapShader的TileMode的时候,没有能够看出效果上的差别。下面把TileMode改为REPEAT,并将Bitmap改为比较小的ic_launcher图斑,大家就可以看出他们的区别在哪里了,代码如下所示。
mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
mPaint = new Paint();
mPaint.setShader(mBitmapShader);
canvas.drawCircle(500,250,200,mPaint);
通过上面的代码,在运行程序,显示效果如图(2)所示。
图(2) TileMode效果
相信通过这样一幅图,大家应该可以理解使用CLAMP拉伸模式模式来创建圆形图像的原因了。
在讲完BitmapShader这个比较特殊的Shader后,再来看看其他比较类似的Shader。
先看一个最简单的,也是最常用的Shader——LinearGradient。直译过来就是线性渐变,没错,他就是一个简单的线性渐变,与Photoshop里面的渐变效果类似,如图(3)所示。
图(3)Photoshop 中的渐变
要使用LinerGradient也非常简单,只需要指定渐变起始颜色就可以了,代码如下所示。
mPaint = new Paint();
mPaint.setShader(new LinearGradient(0,0,mBitmap.getWidth(),mBitmap.getHeight(), Color.BLUE,Color.YELLOW,Shader.TileMode.REPEAT));
canvas.drawRect(0,0,mBitmap.getWidth(),mBitmap.getHeight(),mPaint);
通过以上代码,画出一个Lineardient,效果如图(4)所示,它是一个从(0,0)到(400,400)的一个由蓝色到黄色的渐变效果。
图(4)LinearGradient效果
LinearGradient方法参数中的TileMode与在BitmapShader中的含义基本相同,这里将绘制矩形的大小设置为与渐变图像大小相同,所以没有看见REPEAT的效果。如果将图形扩大,REPEAT的效果就出来了,如图(5)所示。
图(5) REPEAT效果
其他几种渐变模式LinearGradient基本相同,只是渐变的显示效果不同而已,这里就不做过多的介绍了。
其实,这些渐变效果通常不用直接使用在程序里。通常情况下,把这种渐变效果作为一个遮罩层来使用,同时结合前面的PorterDuffxfermode。这样处理后,遮罩层就不再是一个生硬的图形,而是一个具有渐变效果的图层。这样处理的效果会更加柔和、更加自然。下面这个实例就演示了如何使用LinearGradient和PorterDuffXfermode来创建一个具有倒影效果的图片,效果如图(6)所示。
图(6)图片倒影效果
要实现这个效果,首先需要把原图复制一进行翻转,代码如下所示。
mSrcBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.asdfg);
Matrix matrix = new Matrix();
matrix.setScale(1F,-1F);
mRefBitmap = Bitmap.createBitmap(mSrcBitmap,0,0,mSrcBitmap.getWidth(),mSrcBitmap.getHeight(),matrix,true);
需要注意的是,使用matrix.setScale(1F,-1F)方法来实现图片的垂直翻转,这是一个非常有用的技巧,避免了使用选择变换的复制计算。其原理相信大家只要将值带入前面讲解图像图形变换矩阵计算公式中,就知道是为什么了。同理还可以实现图像的水平翻转。
在onDraw()方法中,首先绘制两张图片即原图和倒影图,只是这个时候还未绘制渐变层,因此倒影图与原图的透明度相同。接下来,在倒影图上面绘制一个同样大小的渐变矩形,并通过Mode.DST_IN模式绘制到倒影图上,从而形成一个具有过渡效果的渐变层,完整代码如下所示。
public class ReflectView extends View {
private Bitmap mSrcBitmap,mRefBitmap;
private Paint mPaint;
private PorterDuffXfermode mXfermode;
public ReflectView (Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initRes(context);
}
private void initRes(Context context) {
mSrcBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.asdfg);
Matrix matrix = new Matrix();
matrix.setScale(1F,-1F);
mRefBitmap = Bitmap.createBitmap(mSrcBitmap,0,0,mSrcBitmap.getWidth(),mSrcBitmap.getHeight(),matrix,true);
mPaint = new Paint();
mPaint.setShader(new LinearGradient(0,mSrcBitmap.getHeight(),0,mSrcBitmap.getHeight()+mSrcBitmap.getHeight()/4,0XDD000000,0X10000000, Shader.TileMode.CLAMP));
mXfermode =new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(mSrcBitmap,0,0,null);
canvas.drawBitmap(mRefBitmap,0,mSrcBitmap.getHeight(),null);
mPaint.setXfermode(mXfermode);
//绘制渐变效果矩形
canvas.drawRect(0,mSrcBitmap.getHeight(),mRefBitmap.getWidth(),mSrcBitmap.getHeight()*2,mPaint);
mPaint.setXfermode(null);
}
}