我们在生活中经常会使用到一些渐变效果如:
那么在自定义View的时候我们如何在我们的View上添加这种效果呢?
并且我们在特效中经常看到一些就是背景颜色仿佛在流动的字这种效果又是如何实现的呢?
这些都是使用了Shader这个类来完成的。
首先我们介绍Shader有哪些种类,每种Shader能够实现什么效果。首先我们要知道所有的渐变效果类都是继承了Shader这个类的。也就是Shader是所有渐变类的父类。(策略模式)
LinearGradient 线性渐变
效果展示:
从效果中我们可以看出,线性渐变是从左上角渐变到右下角。而不是从上到下或者从左到右的。那么怎么改变其渐变的方向呢?这个稍后再揭晓~
展示代码:
//注释:之后的代码展示只展示"主要代码",其他代码为该代码片段。
public class ShaderView extends View {
private int mViewWidth;
private int mViewHeight;
//创建画笔
private final Paint mPaint = new Paint();
public ShaderView(Context context) {
this(context,null);
}
public ShaderView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public ShaderView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView(){
//设置笔触类型
mPaint.setStyle(Paint.Style.FILL);
//抗锯齿
mPaint.setAntiAlias(true);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//获取View的大小
mViewWidth = w;
mViewHeight = h;
//主要代码:线性渐变
LinearGradient linearGradient = new LinearGradient(
0,0,w,h, Color.RED,Color.GREEN, Shader.TileMode.CLAMP
);
//添加到画笔中
mPaint.setShader(linearGradient);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//在画布上绘制矩形
canvas.drawRect(0,0,mViewWidth,mViewHeight,mPaint);
}
}
//原理:首先设置渐变的范围(左上角和右下角的坐标),之后设置渐变的颜色(关于颜色,官方给的构造方法默认是必须有两种颜色,那么怎么设置多种颜色呢,我们稍后会讲解到)。最后设置渐变的模式(首先讲解下作用:当绘制的范围,超出了渐变的范围的时候,剩余部分会根据渐变的模式,进行填充。详情见后文,现在知道就可以了,为拉伸模式)。将其放入画笔中,将其绘制在画布上。
protected void onSizeChanged(int w, int h,
int oldw, int oldh) {
...(上面代码参照第一份代码省略)
//主要代码:光束渐变
int centerX = w/2;
int centerY = h/2;
int radium = Math.min(w,h)/2;
RadialGradient radialGradient = new RadialGradient(
centerX,centerY,radium,Color.YELLOW,Color.GREEN,
Shader.TileMode.CLAMP
);
mPaint.setShader(radialGradient);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制圆形
int centerX = mViewWidth/2;
int centerY = mViewHeight/2;
int radium = Math.min(mViewWidth,mViewHeight)/2;
canvas.drawCircle(centerX,centerY,radium,mPaint);
}
//原理:首先设置渐变的中心点和渐变的半径,然后设置渐变的颜色,及渐变的类型。
protected void onSizeChanged(int w, int h,
int oldw, int oldh) {
...(上面代码参照第一份代码省略)
//主要代码:梯度渐变
int centerX = w/2;
int centerY = h/2;
SweepGradient sweepGradient = new SweepGradient(
centerX,centerY,Color.YELLOW,Color.BLUE
);
mPaint.setShader(sweepGradient);
}
//原理:设置渐变的中心点,然后设置渐变的颜色,最后设置渐变的类型。
BitmapShader 位图渐变
效果展示:
这就是从一张完整的图中截取了圆形的部分0 0。
展示代码:
protected void onSizeChanged(int w, int h,
int oldw, int oldh) {
...(上面代码参照第一份代码省略)
//主要代码:位图渐变
//第一步:首先获取图片
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.shader);
BitmapShader bitmapShader = new BitmapShader(bitmap,CLAMP,CLAMP);
mPaint.setShader(bitmapShader);
//简单原理:将图片设置为渐变的填充物(并且图片左上角坐标默认0,0点,大小为图片的宽高),然后设置横轴、纵轴渐变的模式。(关于详细原理,等讲完Shader的类型时候再进行解释)
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制圆形
int centerX = mViewWidth/2;
int centerY = mViewHeight/2;
int radium = Math.min(mViewWidth,mViewHeight)/2;
canvas.drawCircle(centerX,centerY,radium,mPaint);
}
private static final int BITMAP_WIDTH = 400;
private static final int BITMAP_HEIGHT = 300;
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
...(上面代码省略)
//主要代码:位图渐变
//第一步:首先获取图片
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.shader);
BitmapShader bitmapShader = new BitmapShader(bitmap,CLAMP,CLAMP);
//由于当画布移动的时候,图片的显示的起始点会根据画布的起始点移动,所以需要通过Matrix对图片进行平移
Matrix matrix = new Matrix();
matrix.postTranslate(-BITMAP_WIDTH/2,-BITMAP_HEIGHT/2);
bitmapShader.setLocalMatrix(matrix);
//原理:将图片设置为渐变的填充物,然后设置横轴、纵轴渐变的模式。
//线性渐变,第二种创建Shader的构造方法。
LinearGradient linearGradient = new LinearGradient(
0,0,BITMAP_WIDTH,BITMAP_HEIGHT,new int[]{Color.WHITE,Color.TRANSPARENT},
null,MIRROR
);
//混合渐变
//原理:合并两个Shader,并设置合并的方式。也就是PorterDuff.Mode,关于ProterDuff我们下篇文章会具体提到,现在只需知道DST_IN是显示先绘制的Shader。
ComposeShader shader = new ComposeShader(bitmapShader,linearGradient, PorterDuff.Mode.DST_IN);
mPaint.setShader(shader);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//设置画布居中
int centerX = mViewWidth/2;
int centerY = mViewHeight/2;
canvas.translate(centerX,centerY);
//设置绘制的范围
RectF rectF = new RectF(-BITMAP_WIDTH/2,-BITMAP_HEIGHT/2,
BITMAP_WIDTH/2,BITMAP_HEIGHT/2);
//绘制圆角矩形
canvas.drawRoundRect(rectF,30,30,mPaint);
}
首先我们知道Shader是一种渐变,那么渐变的绘制是由画笔控制的。所以画笔就有Paint.setShader()的方法来设置画笔的渐变模式。之后利用设置过Shader的Paint绘制图形。所以,我们可以将Shader想象成一种特殊的颜色,在绘制图形的时候,都需要设定图形的颜色类型。
所以简单使用原理就是(伪代码):
Paint paint = new Paint();
Shader shader = new Shader();
//将创建的Shader放入画笔
paint.setShader(shader);
//利用画笔,绘制图形
canvas.drawRect(x0,y0,x1,y1,paint);
1、LinearGradient、RadialGradient、SweepGradient使用统一的构造方法(统一指的是原理,这里以LinearGradient为例)
//第一种构造方法
/** *float x0,y0,x1,y1:表示渐变的范围,这四个参数表示矩形的左上角和右下角坐标。(注:在LinearGraident 渐变的方向为(x0,y0)到(x1,y1)) *int color0,int color1:构造方法默认只能设置两种颜色 *TileMode:当绘制范围超出渐变范围的时候,剩下的范围根据选择的Mode进行填充。 */
LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, Shader.TileMode tile)
/** *int [] colors:颜色数组,用来设置多种渐变颜色 *float [] pos:颜色权重,在颜色数组中,每种颜色显示的多少。可以为null,默认为线性平均分配。 */
LinearGradient(float x0, float y0, float x1, float y1, int[] colors, float[] positions, Shader.TileMode tile)
2、BitmapShader的构造方法
/** * Bitmap:图片对象,并且Shader的大小为图片的宽高。并且图片显示区域默认在canvas的(0,0)点。(注意:图片默认是在canvas的(0,0)点,而不是绘制图形完成后,图形的左上角点) * TileMode tilex:当绘制区域的长度超出了Shader的长度的时候,利用该模式来填充剩余部分 * TileMode tileY:当绘制区域的高度超出了Shader的高度的时候,利用该模式填充剩余部分 */
BitmapShader(Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)
3、ComposeShader的构造方法
/** *Shader shaderA,shaderB:表示继承于Shader的渐变类型 *Xfermode:表示当shaderA,shaderB渐变区域重叠的时候,做出的模式 */
ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode)
/** *PorterDuff.Mode:作用与Xfermode相同,不同的地方在于Xfermode是对象可自定义,而PorterDuff为枚举类。 */
ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode)
作用:当渐变区域小于绘制的图形区域的时候,剩余部分就需要按照某种模式来进行填充。
TileMode的种类:
CLAMP:伸展模式。表示利用最后一个元素的颜色填充剩余部分。
效果展示:(LinearGradient从左向右渐变)
展示代码:
//线性渐变
LinearGradient linearGradient = new LinearGradient(
0, 0, mViewWidth, 0, Color.RED, Color.GREEN, CLAMP
);
mPaint.setShader(linearGradient);
原理解析:
我们比较之前线性渐变的代码,我们可以发现,只是在构造方法中将y1由mViewHeight变成了0而已。然后这就表示LinearGradient渐变的方向是从(0,0)点到(mViewWidth,0)点。之后由于绘制的图形区域是一个矩形,而渐变区域只是一条线。所以就会利用伸展模式填充矩形的剩余部分。利用渐变区域最边缘的颜色进行填充
REPEAT: 重复模式。 表示重复绘制渐变区域填充剩余部分。
效果展示:
(额,密集恐惧症慎入~~~)
展示代码:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher);
BitmapShader bitmapShader = new BitmapShader(bitmap,REPEAT,REPEAT);
mPaint.setShader(bitmapShader);
原理解析:重复利用渐变区域填充剩余部分
MIRROR:镜像模式。 根据x轴或y轴进行翻转。
效果展示:
代码展示:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.shader);
BitmapShader bitmapShader = new BitmapShader(bitmap,MIRROR,MIRROR);
mPaint.setShader(bitmapShader);
制作步骤:
1、首先由于绘制的是文字的背景,所以要继承TextView
2、通过getPaint()拿到TextView的画笔。(注意:不是new Paint())
3、设置LinearGradient的渐变范围
4、利用LinearGraident设置线性渐变的背景。
5、利用Matrix平移LinearGradient的渐变区域,从而形成流动的效果。
详细代码:
public class ShaderTextView extends TextView {
private Paint mPaint;
private LinearGradient mLinearGradient;
private Matrix mMatrix;
private float mViewWidth;
private int mTranslate;
public ShaderTextView(Context context) {
this(context,null);
}
public ShaderTextView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public ShaderTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initWidget();
}
private void initWidget(){
mPaint = getPaint();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mViewWidth = w;
//设置线性渐变
mLinearGradient = new LinearGradient(0,0,mViewWidth,0,
new int[]{Color.RED,Color.GREEN,Color.BLUE},null, Shader.TileMode.REPEAT);
mPaint.setShader(mLinearGradient);
//创建矩阵
mMatrix = new Matrix();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//设置位移
if (mTranslate > mViewWidth){
mTranslate = (int)-mViewWidth;
}
else {
mTranslate += mViewWidth/120;
}
mMatrix.setTranslate(mTranslate,0);
mLinearGradient.setLocalMatrix(mMatrix);
invalidate();
}
}