Paint着色器简介
Paint着色器会对Paint绘制的区域进行填充。
通过Paint.setShader()方法设置着色器,Paint着色器有如下几种:
- BitmapShader
- LinearGradient
- SweepGradient
- RadialGradient
- ComposeShader
BitmapShader
BitmapShader使用Bitmap来进行填充,下面来看下它的构造方法:
public BitmapShader(@NonNull Bitmap bitmap, @NonNull TileMode tileX, @NonNull TileMode tileY)
bitmap为填充的位图
tileX为X轴方向位图填充方式
tileY为Y轴方向位图填充方式
TileMode表示以何种方式来填充,有如下3种类型:
- CLAMP:当位图的大小小于Paint绘制区域时,以边界区域进行填充
- MIRROR:当位图的大小小于Paint绘制区域时,以位图镜像方式进行填充
- REPEAT:当位图的大小小于Paint绘制区域时,位图重复进行填充
着色器可通过setLocalMatrix()来设置Matrix,通过Matrix来对位图进行平移、缩放、旋转等矩阵操作
圆形头像效果
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
//创建BitmapShader
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
paint.setShader(bitmapShader);
//绘制矩形区域大小为图片大小
canvas.drawRect(0, 0, bitmap.getWidth(), bitmap.getHeight(), paint);
canvas.translate(0, bitmap.getHeight() + 20);
//绘制圆形
canvas.drawCircle(bitmap.getHeight() / 2f, bitmap.getHeight() / 2f, bitmap.getHeight() / 2f, paint);
canvas.translate(0, bitmap.getHeight() + 20);
Matrix matrix = new Matrix();
matrix.setTranslate(-(bitmap.getWidth() - bitmap.getHeight()) / 2f, 0);
//设置Matrix,通过Matrix来平移位图
bitmapShader.setLocalMatrix(matrix);
//绘制圆形
canvas.drawCircle(bitmap.getHeight() / 2f, bitmap.getHeight() / 2f, bitmap.getHeight() / 2f, paint);
canvas.translate(0, bitmap.getHeight() + 20);
matrix = new Matrix();
matrix.postTranslate(-(bitmap.getWidth() - bitmap.getHeight()) / 2f, 0);
matrix.preRotate(180, bitmap.getWidth() / 2f, bitmap.getHeight() / 2f);
//设置Matrix,通过Matrix来平移、旋转位图
bitmapShader.setLocalMatrix(matrix);
//绘制圆形
canvas.drawCircle(bitmap.getHeight() / 2f, bitmap.getHeight() / 2f, bitmap.getHeight() / 2f, paint);
放大镜效果
public class ScaleView extends View {
//原图
private Bitmap mBitmap;
//放大后的图片
private Bitmap mScaleBitmap;
private Paint mPaint;
//圆形放大镜半径
private final int RADIUS = 100;
//图片放大比例
private final int scale = 3;
//原图触摸点
private int mCenterX;
private int mCenterY;
private BitmapShader mBitmapShader;
private Matrix mShaderMatrix;
public ScaleView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
//原图
mBitmap = ((BitmapDrawable) getResources().getDrawable(R.mipmap.a)).getBitmap();
Matrix matrix = new Matrix();
matrix.postScale(scale, scale);
//原图放大后的图片
mScaleBitmap = Bitmap.createBitmap(mBitmap, 0, 0, mBitmap.getWidth(), mBitmap.getHeight(), matrix, false);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
//使用放大后的图片来填充画笔
mBitmapShader = new BitmapShader(mScaleBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mShaderMatrix = new Matrix();
mPaint.setShader(mBitmapShader);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制原图
canvas.drawBitmap(mBitmap, 0, 0, null);
//将原图触摸坐标装换为对应放大图片的坐标
mShaderMatrix.setTranslate(mCenterX * (1 - scale), mCenterY * (1 - scale));
mBitmapShader.setLocalMatrix(mShaderMatrix);
//绘制圆形放大镜
canvas.drawCircle(mCenterX, mCenterY, RADIUS, mPaint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mCenterX = (int) event.getX();
mCenterY = (int) event.getY();
invalidate();
return true;
}
}
LinearGradient
LinearGradient使用线性渐变色来填充Paint绘制区域,下面来看看它的构造方法:
public LinearGradient(float x0, float y0, float x1, float y1,
@NonNull @ColorInt int[] colors,
@Nullable float[] positions, @NonNull TileMode tile)
x0、y0表示渐变色起始坐标点,x1、y1表示渐变色结束坐标点
colors用于设置颜色
positions表示颜色渐变的比例,其值为[0~1],并且数组长度必须和colors一样
tile表示填充模式
跑马灯效果
public class LinearGradientTextView extends View {
private final int[] colors = new int[]{0x22ffffff, 0xffffffff, 0x22ffffff};
private String text = "在 Android 开发者技能中,如果想进大厂,一般拥有较好的学历可能有优势一些。" +
"但是如果你靠硬实力也是有机会的,例如死磕Framework。";
private final Paint mPaint;
private final List<String> mLineTexts;
private final List<Float> mLineWidths;
private final List<Float> mLineBaselines;
private LinearGradient mLinearGradient;
private Matrix mMatrix;
private float mLinearGradientWidth;
private int mLinearGradientLine;
private float mLinearGradientStart;
public LinearGradientTextView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mLineTexts = new ArrayList<>();
mLineWidths = new ArrayList<>();
mLineBaselines = new ArrayList<>();
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setTextSize(60);
mPaint.setColor(Color.WHITE);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//清空数据
mLineTexts.clear();
mLineBaselines.clear();
mLineWidths.clear();
int start = 0;
//获取FontMetrics
Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
//第一行文字的中心线
float centerY = fontMetrics.bottom - fontMetrics.top;
do {
float[] lineWidth = new float[1];
//计算指定宽度中可绘制文字数量,并获取该行宽
int count = mPaint.breakText(text,
start,
text.length(),
true,
getWidth(), lineWidth);
//保存当前行文本
mLineTexts.add(text.substring(start, start + count));
//保存当前行宽度
mLineWidths.add(lineWidth[0]);
//计算当前行基线坐标
mLineBaselines.add(centerY - (fontMetrics.bottom + fontMetrics.top) / 2f);
//下一行的中线坐标
centerY += fontMetrics.bottom - fontMetrics.top;
//下一行文本起始索引位置
start += count;
} while (start < text.length());
//5个文字的宽度
mLinearGradientWidth = mPaint.measureText(text) / text.length() * 5;
mLinearGradientStart = -mLinearGradientWidth;
//颜色渐变长度为5个文字的宽度
mLinearGradient = new LinearGradient(0,
0, mLinearGradientWidth,
0, colors, new float[]{0, 0.5f, 1}, Shader.TileMode.CLAMP);
mMatrix = new Matrix();
mLinearGradient.setLocalMatrix(mMatrix);
//设置Shader
mPaint.setShader(mLinearGradient);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制每一行
for (int i = 0; i < mLineTexts.size(); i++) {
//如果当前行使用颜色渐变
if (i == mLinearGradientLine) {
//平移LinearGradient
mMatrix.setTranslate(mLinearGradientStart, 0);
float DELTA = 20;
mLinearGradientStart += DELTA;
if (mLinearGradientStart > mLineWidths.get(i)) {
mLinearGradientStart = -mLinearGradientWidth;
mLinearGradientLine += 1;
if (mLinearGradientLine == mLineTexts.size())
mLinearGradientLine = 0;
}
} else {
//平移LinearGradient
mMatrix.setTranslate(-mLinearGradientWidth, 0);
}
mLinearGradient.setLocalMatrix(mMatrix);
//绘制文本
canvas.drawText(mLineTexts.get(i), 0, mLineBaselines.get(i), mPaint);
}
postInvalidateDelayed(16);
}
}
SweepGradient
SweepGradient使用梯度渐变色来填充Paint绘制区域,下面来看看其构造方法:
public SweepGradient(float cx, float cy, @NonNull @ColorInt int[] colors,
@Nullable float[] positions)
cx、cy表示渐变的中心点
colors表示渐变色
positions表示颜色渐变比例,范围为[0~1],可以为null,如果不会null则数组长度需和colors长度一样
雷达效果
//用于改变旋转角度
Matrix matrix = new Matrix();
private void drawSweepGradient(Canvas canvas) {
float centerX = 200;
float centerY = 200;
float radius = 200;
int[] colors = new int[]{
0x0A0000ff,
0x1A0000ff,
0x2A0000ff,
0x3A0000ff,
0x4A0000ff,
0x5A0000ff};
//创建SweepGradient
SweepGradient sweepGradient = new SweepGradient(centerX, centerY, colors, null);
//每次顺时针旋转3°
matrix.postRotate(3, centerX, centerY);
sweepGradient.setLocalMatrix(matrix);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
//设置Shader
paint.setShader(sweepGradient);
//使用SweepGradient来填充绘制的圆形
canvas.drawCircle(centerX, centerY, radius, paint);
//每隔20ms绘制一次
postInvalidateDelayed(20);
}
RadialGradient
RadialGradient使用环形渲染填充Paint绘制区域,下面来看看其构造方法:
public RadialGradient(float centerX, float centerY, float radius,
@NonNull @ColorInt int[] colors, @Nullable float[] stops,
@NonNull TileMode tileMode)
centerX、centerY表示环形中点 radius表示环形半径大小 colors表示环形渐变色 stops表示渐变色颜色比例 tileMode为填充模式
水波纹效果
点击按钮时会有一个水波纹效果,该效果可使用RadialGradient来实现,通过不断放大RadialGradient即可实现
private void drawRadialGradient(Canvas canvas) {
//按钮宽
int width = 600;
//按钮长
int height = 200;
//模拟点击位置
float centerX = width / 4f;
float centerY = height / 4f;
//水波纹最小半径
float minRadius = height / 2f;
//水波纹最大半径
float maxRadius = width * 1.0f;
//水波纹渐变色
int[] colors = new int[]{
0x5Aff0000,
0x00999999
};
//创建环形着色器
if (radialGradient == null)
radialGradient = new RadialGradient(centerX, centerY,
minRadius,
colors,
null, Shader.TileMode.CLAMP);
//使用Matrix来缩放环形着色器
matrix.setScale(scale, scale, centerX, centerY);
//修改环形着色器缩放比例
scale += 0.2f;
boolean stop = false;
if (scale > maxRadius / minRadius) {
scale = 1;
stop = true;
}
radialGradient.setLocalMatrix(matrix);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.parseColor("#666666"));
paint.setShader(null);
//绘制矩形用于模拟按钮
canvas.drawRect(0, 0, 600, 200, paint);
//使用环形着色器绘制矩形
paint.setShader(radialGradient);
canvas.drawRect(0, 0, 600, 200, paint);
//不断绘制,形成动画效果
postInvalidateDelayed(stop ? 1000 : 20);
}
ComposeShader
ComposeShader用于组合两个着色器,下面来看其构造方法:
public ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB,
@NonNull PorterDuff.Mode mode)
mode表示组合shaderA和shaderB的方式,PoterDuff将在下篇文章介绍。
其中shaderA表示PorterDuff中的“dst”,shaderB表示“src”
倒影效果
倒影效果:将一张图片向下进行镜像翻转,然后使用线性渐变色作用于倒影上。使用BitmapShader实现向下镜像翻转,LinearGradient实现线性渐变色。
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.xyjy2);
//以镜像形式进行位图填充
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR);
//由于镜像图片位于原图下面,所以渐变色顶部为原图高度大小,底部为原图高度的2倍
LinearGradient linearGradient = new LinearGradient(0, bitmap.getHeight(), 0, 2 * bitmap.getHeight(),
new int[]{0x0Affffff, 0xBAffffff, 0xffffffff}, new float[]{0, 0.5f, 1}, Shader.TileMode.CLAMP);
//组合BitmapShader和LinearGradient
ComposeShader composeShader = new ComposeShader(bitmapShader, linearGradient, PorterDuff.Mode.SRC_ATOP);
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setShader(composeShader);
//绘制矩形,矩形宽为图片宽,高为图片高的2倍
canvas.drawRect(0, 0,
bitmap.getWidth(), bitmap.getHeight() * 2,
paint);