介绍:
Shader is the based class for objects that return horizontal spans of colors
during drawing. A subclass of Shader is installed in a Paint calling
paint.setShader(shader). After that any object (other than a bitmap) that is
drawn with that paint will get its color(s) from the shader.
Shader共有五个子类:
- BitmapShader 瓷砖渐变
- LinearGradient 线性渐变
- RadialGradient 光束渐变
- SweepGradient 梯度渐变
- ComposeShader 混合渐变
1.BitmapShader 瓷砖渐变
Shader used to draw a bitmap as a texture. The bitmap can be repeated or
mirrored by setting the tiling mode.
一个构造方法:
public BitmapShader(@NonNull Bitmap bitmap, @NonNull TileMode tileX, @NonNull TileMode tileY) {}
参数:
- bitmap:图片
- tilex X轴瓷砖模式,共三种
- tileY y轴瓷砖模式,共三种
3种模式:
- Shader.TileMode.CLAMP 拉伸模式
CLAMP 模式就是在View的宽度或者高度大于图片的宽或高时,在绘制图片的最后一帧拉伸展示。
public class BitmapShader_CLAMP extends View{
private Paint paint;
public BitmapShader_CLAMP(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initPaint();
}
private void initPaint() {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.img);
BitmapShader bitmapShader = new BitmapShader(bitmap, android.graphics.Shader.TileMode.CLAMP, android.graphics.Shader.TileMode.CLAMP);
paint.setShader(bitmapShader);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
setLayerType(View.LAYER_TYPE_SOFTWARE,null); //去掉硬件加速
int x = getWidth() / 2;
int y = getHeight() / 2;
int radius = Math.min(getWidth(),getHeight()) / 2;
canvas.drawCircle(x,y,radius,paint);
}
private void mMeasure(int widthMeasureSpec, int heightMeasureSpec){
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if(widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST){
setMeasuredDimension(720,500);
}else if(widthMode == MeasureSpec.AT_MOST){
setMeasuredDimension(720,heightSize);
}else if(heightMode == MeasureSpec.AT_MOST){
setMeasuredDimension(widthSize,500);
}
}
}
- Shader.TileMode.MIRROR 镜像模式
MIRROR 模式当view的高度或者宽度大于图片的高或宽时,先倒立,再重复绘制。
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR);
- Shader.TileMode.REPEAT 重复模式
REPEAT 模式与MIRROR模式差不多,只是没有倒立显示。只有重复
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, android.graphics.Shader.TileMode.REPEAT);
2.线性渐变 LinearGradient
共有两个构造方法:
public LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[], @Nullable float positions[], @NonNull TileMode tile) {}
public LinearGradient(float x0, float y0, float x1, float y1,@ColorInt int color0, @ColorInt int color1, @NonNull TileMode tile) {}
区别:
- colors可以指定多种颜色
- positions指定渐变位置
参数:
- x0: 开始绘制的x轴坐标
- y0: 开始绘制的y轴坐标
- x1: 结束绘制的x轴坐标
- y1: 结束绘制的y轴坐标
- color0: 渐变的颜色
- color1: 渐变的颜色
- tilemode: 瓷砖模式
构造方法:
- 方法一
只能指定两种渐变颜色
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/**
* 参数:
* x0: 开始绘制的x轴坐标
* y0: 开始绘制的y轴坐标
* x1: 结束绘制的x轴坐标
* y1: 结束绘制的y轴坐标
* color0: 渐变的颜色
* color1: 渐变的颜色
* tilemode: 瓷砖模式
*/
LinearGradient linearGradient = new LinearGradient(0,0,300,300, Color.RED,Color.BLUE, Shader.TileMode.REPEAT);
paint.setShader(linearGradient);
canvas.drawRect(0,0,Math.min(getWidth(),getHeight()),Math.min(getWidth(),getHeight()),paint);
}
- 方法二
可以指定多种渐变颜色,多个开始渐变的位置
int[] colors = new int[]{Color.BLUE,Color.RED,Color.YELLOW};
float[] positions = new float[]{0.0f,0.5f,1.0f};
LinearGradient linearGradient = new LinearGradient(0,0,600,600, colors,positions, Shader.TileMode.REPEAT);
3.光束渐变
有两个构建方法:
public RadialGradient(float centerX, float centerY, float radius,@NonNull @ColorInt int colors[], @Nullable float stops[],@NonNull TileMode tileMode) {}
public RadialGradient(float centerX, float centerY, float radius,@ColorInt int centerColor, @ColorInt int edgeColor, @NonNull TileMode tileMode) {}
区别:
- colors可以指定多种颜色
- positions指定渐变位置
参数:
- centerX 圆心的X轴坐标
- centerY 圆点的Y轴坐标
- radius 半径
- centerColor 中心颜色
- edgeColor 边缘颜色
- tileMode 瓷砖模式
两种构造方法
- 方法一
只能指定两种渐变颜色
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/**
* centerX 圆心的X轴坐标
* centerY 圆点的Y轴坐标
* radius 半径
* centerColor 中心颜色
* edgeColor 边缘颜色
* tileMode 瓷砖模式
*/
RadialGradient radialGradient = new RadialGradient(getWidth()/2,getHeight()/2,Math.min(getWidth(),getHeight())/2,Color.RED,Color.BLACK, Shader.TileMode.CLAMP);
paint.setShader(radialGradient);
canvas.drawRect(0,0,getWidth(),Math.min(getWidth(),getHeight()),paint);
}
- 方法二
可以指定多种渐变颜色,多个开始渐变的位置
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
final int[] colors = new int[]{Color.BLACK,Color.RED ,Color.BLACK};
final float[] positions = new float[]{0f, 0.5f ,1f};
RadialGradient radialGradient = new RadialGradient(getWidth()/2,getHeight()/2,Math.min(getWidth(),getHeight())/2,colors,positions, Shader.TileMode.CLAMP);
paint.setShader(radialGradient);
canvas.drawRect(0,0,getWidth(),Math.min(getWidth(),getHeight()),paint);
}
4.梯度渐变
同样有两个构造方法
public SweepGradient(float cx, float cy,@NonNull @ColorInt int colors[], @Nullable float positions[]) {}
public SweepGradient(float cx, float cy, @ColorInt int color0, @ColorInt int color1) {}
区别:
- colors可以指定多种颜色
- positions指定渐变位置
参数:
- cx 中心点的X轴坐标
- cy 中心点的Y轴坐标
- color0 开始渐变的颜色
- color1 结束渐变的颜色
两个构建方法:
- 方法一
只能指定两种渐变颜色
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/**
* cx 中心点的X轴坐标
* cy 中心点的Y轴坐标
* color0 开始渐变的颜色
* color1 结束渐变的颜色
*/
SweepGradient sweepGradient = new SweepGradient(Math.min(getWidth(),getHeight())/2,Math.min(getWidth(),getHeight())/2,Color.RED,Color.BLUE);
paint.setShader(sweepGradient);
canvas.drawRect(0,0,Math.min(getWidth(),getHeight()),Math.min(getWidth(),getHeight()),paint);
}
- 方法二
可以指定多种渐变颜色,多个开始渐变的位置
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
final int[] colors = new int[]{Color.MAGENTA, Color.CYAN,Color.YELLOW,Color.BLUE ,Color.RED};
final float[] positions = new float[]{0f, 0.25f,0.5f ,0.75f,1f};
SweepGradient sweepGradient = new SweepGradient(Math.min(getWidth(),getHeight())/2,Math.min(getWidth(),getHeight())/2,colors,positions);
paint.setShader(sweepGradient);
canvas.drawRect(0,0,Math.min(getWidth(),getHeight()),Math.min(getWidth(),getHeight()),paint);
}
5.混合渐变
A subclass of shader that returns the composition of two other shaders, combined by
an {@link android.graphics.Xfermode} subclass.
顾名思义,就是将多种渐变模式混合在一起。
两个构建方法:
public ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB, @NonNull Xfermode mode) {}
public ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB,@NonNull PorterDuff.Mode mode) {}
区别:
- 可以指定PorterDuff
- 设置Xfermode
参数:
- shaderA 渐变模式
- shaderB 渐变模式
- mode 混合模式
两个构建方法:
- 方法一
可以指定一种混合模式,梯度渐变 + 光束渐变,要关闭硬件加速
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
setLayerType(LAYER_TYPE_SOFTWARE,null);
SweepGradient sweepGradient = new SweepGradient(getWidth()/2,getHeight()/2,Color.RED,Color.BLUE);
RadialGradient radialGradient = new RadialGradient(getWidth()/2,getHeight()/2,Math.min(getWidth(),getHeight())/2,Color.RED,Color.BLACK, Shader.TileMode.CLAMP);
ComposeShader composeShader = new ComposeShader(sweepGradient,radialGradient, PorterDuff.Mode.SCREEN);
paint.setShader(composeShader);
canvas.drawRect(0,0,getWidth(),getHeight(),paint);
}
- 方法二
可以指定一种Xfermode
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
setLayerType(LAYER_TYPE_SOFTWARE,null);
SweepGradient sweepGradient = new SweepGradient(getWidth()/2,getHeight()/2,Color.RED,Color.BLUE);
RadialGradient radialGradient = new RadialGradient(getWidth()/2,getHeight()/2,Math.min(getWidth(),getHeight())/2,Color.RED,Color.BLACK, Shader.TileMode.CLAMP);
ComposeShader composeShader = new ComposeShader(sweepGradient,radialGradient,new PorterDuffXfermode(PorterDuff.Mode.ADD));
paint.setShader(composeShader);
canvas.drawRect(0,0,getWidth(),getHeight(),paint);
}
Demo
public class ReflectView extends View {
private Paint mPaint;
private Bitmap dstBitmap, srcBitmap;
private PorterDuffXfermode xfermode;
private int x, y;
public ReflectView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
//原图Bitmap
dstBitmap = decodeBitmapFormRes(getResources(), R.drawable.timg,540, 960);
//垂直翻转
Matrix matrix = new Matrix();
matrix.setScale(1f, -1f);
//图片向右倾斜
matrix.preSkew(-0.5f, 0);
//倒影Bitmap
srcBitmap = Bitmap.createBitmap(dstBitmap, 0, 0, dstBitmap.getWidth(), dstBitmap.getHeight(), matrix, true);
//初始化画笔
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
//屏幕宽度
int screenW = getResources().getDisplayMetrics().widthPixels;
//起始点
x = screenW / 2 - dstBitmap.getWidth() / 2;
y = 0;
//设置渐变矩形
mPaint.setShader(new LinearGradient(x, dstBitmap.getHeight(), x, dstBitmap.getHeight() + dstBitmap.getHeight() / 2, 0xDD000000, Color.TRANSPARENT, Shader.TileMode.CLAMP));
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
setLayerType(LAYER_TYPE_SOFTWARE,null);
//绘制背景
canvas.drawColor(Color.BLACK);
//绘制原图
canvas.drawBitmap(dstBitmap, x, y, null);
//绘制倒影图片
canvas.drawBitmap(srcBitmap, x, dstBitmap.getHeight(), null);
mPaint.setXfermode(xfermode);
//绘制渐变层
canvas.drawRect(x, dstBitmap.getHeight(), x + dstBitmap.getWidth()+x , dstBitmap.getHeight() * 2, mPaint);
mPaint.setXfermode(null);
}
/**
* 测量
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int wSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int wSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int hSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int hSpecSize = MeasureSpec.getSize(heightMeasureSpec);
if (wSpecMode == MeasureSpec.AT_MOST && hSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(300, 900);
} else if (wSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(300, hSpecSize);
} else if (hSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(wSpecSize, 900);
}
}
/**
* 图片的缩放
*/
private Bitmap decodeBitmapFormRes(Resources resources, int resId, int targetWidth, int targetHeight) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
options.inJustDecodeBounds = false;
BitmapFactory.decodeResource(resources, resId, options);
int inSample = calculateInSample(options, targetWidth, targetHeight);
options.inSampleSize = inSample;
return BitmapFactory.decodeResource(resources, resId, options);
}
private int calculateInSample(BitmapFactory.Options options, int targetWidth, int targetHeight) {
if (targetWidth <= 0 || targetHeight <= 0) {
return 1;
}
int inSample = 1;
final int rawWidth = options.outWidth;
final int rawHeight = options.outHeight;
if (rawWidth > targetWidth || rawHeight > targetHeight) {
final int halfWidth = rawWidth / 2;
final int halfHeight = rawHeight / 2;
while ((halfWidth / inSample >= targetWidth) && (halfHeight / inSample >= targetHeight)) {
inSample *= 2;
}
}
return inSample;
}
}